ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • The Basics
    iOS/Swift 공식문서 2022. 2. 11. 21:47

    Constants & Variables

    선언

    상수는 let, 변수는 var로 선언한다. 값을 할당하면 해당 값의 타입으로 상수/변수의 타입이 정해진다.
    (값이 바뀔 가능성이 없다면 항상 let으로 선언하자. var는 값이 변할 수 밖에 없는 경우만 사용하자.)

    let a = 0
    var b = 10

     

    상수 값을 바꾸려 하면 compile-time error가 난다.

    let a = 1
    a = 2
    // error: cannot assign to value: 'a' is a 'let' constant

    콤마(,)로 구분하여 여러 상수/변수를 선언하는 것도 가능하다.

    var x = 0.0, y = 0.0, z = 0.0

     

    Type Annotation

    타입을 따로 지정해줘야 하는 경우가 있을 때 사용한다. 예를 들면 선언만 하고 값을 할당하지 않을 때, 혹은 우변의 타입과는 다른 타입으로 상수/변수를 선언하고자 할 때가 있다. 이 때는 변수명 뒤에 콜론(:) + 공백( )과 타입을 적어주면 된다.

    var a: String

     

    같은 타입의 여러 상수나 변수를 한 번에 선언하는 것도 가능하다.

    var red, green, blue: Double

     

    타입 변경 불가능

    상수/변수는 선언하는 시점의 타입의 값만 가질 수 있다. 다른 타입의 값을 부여하는 것은 불가능하다.

    var a = 1
    a = 1.0
    // error: cannot assign value of type 'Double' to type 'Int'

     

    출력과 String Interpolation

    출력은 print() 함수를 호출하면 된다.
    String 타입이 아닌 값을 다른 문자열에 포함시키고자 한다면, \(상수/변수 이름)을 문자열 안에 적어주면 된다.

    var a = 10
    print("current value of a is \(a)") // current value of a is 10

     

    Semicolon

    JS 처럼 세미콜론은 선택이다. 그러나 한 줄에 여러 statement를 적고자 하면 필요하다.

    let cat = "🐱"; print(cat)
    // Prints "🐱"

     

    Integers

    부호가 있는 정수를 나타내는 Int, 부호가 없는 정수를 나타내는 UInt 타입이 제공된다. 또한 비트 수(8, 16, 32, 64)에 따라서도 여러 타입이 제공된다. 비트 수를 명시하지 않고 Int, UInt를 사용하는 경우 기본 타입이 적용된다. 32 bit 플랫폼의 경우 Int32, UInt32가 기본 타입이고, 64 bit 플랫폼의 경우 Int64, UInt64가 기본 타입이다.

     

    각 타입의 최대/최솟값은 각 타입의 max, min 프로퍼티를 통해 접근할 수 있다.

    let minValue = UInt8.min  // 0
    let maxValue = UInt8.max  // 255

     

    Floating-Point Numbers

    32 bit짜리 소수는 Float, 64 bit짜리 소수는 Double로 선언한다. Double은 최소 소수점 아래 15자리의 정확도를 가지고, Float는 겨우 6자리 정도이다. 가급적 Double을 사용하자.

    var a: Float = 1.22222222222222222
    print(a) // 1.2222222

     

    Type Safety & Type Inference

    Swift는 type-safe language이다. 잘못된 타입의 값을 사용하지 않도록 코드를 컴파일하면서 type check를 하고 mismatch가 있으면 에러를 발생시킨다. 또한 Swift는 Type Inference를 제공하는데, 우리가 할당한 값을 통해 타입을 유추함으로써 우리가 상수/변수를 매번 선언할 때마다 타입을 명시할 필요가 없게 된다.

    let meaningOfLife = 42
    // 정수값이 오면 보통 Int로 추론
    
    let pi = 3.14159
    // 소수의 경우 Double로 추론
    
    let anotherPi = 3 + 0.14159
    // 정수 + 소수 = 소수 => Double로 추론

     

    Numeric Literals

    Integer Literals

    10진법 외에도 2, 8, 16진법으로 표시할 수가 있다. 각각 prefix로 0b, 0o, 0x를 사용한다.

    let decimalInteger = 17
    let binaryInteger = 0b10001       // 17 in binary notation
    let octalInteger = 0o21           // 17 in octal notation
    let hexadecimalInteger = 0x11     // 17 in hexadecimal notation

     

    Floating-Point Literals

    10진법 외에도 16진법으로 표시할 수 있고, 16진법의 경우 prefix로 0x를 사용한다. 10진법의 경우 e를 사용하여 10의 거듭제곱을 곱한 꼴로 나타낼 수 있다.

    var a = 1.25e-2
    print(a) // 1.25 * 10^(-2) = 0.125
    
    a = 1.25e2
    print(a) // 1.25 * 10^2 =  125.0

     

    16진법의 경우 p를 사용하여 2의 거듭제곱을 곱한 꼴로 나타낼 수 있다.

    var a = 0xFp-2
    print(a) // 15 * 2^(-2) = 3.75
    
    a = 0xFp2
    print(a) // 15 * 2^2 = 60.0
    
    a = 0xC.3p0
    print(a) // C.3(16진법) = 12 + 3/16 = 12.1875

     

    가독성을 위해 중간에 밑줄을 끼워넣을 수 있다.

    let paddedDouble = 000123.456
    let oneMillion = 1_000_000
    let justOverOneMillion = 1_000_000.000_000_1

     

    Numeric Type Conversion

    Integer Conversion

    정확히 같은 타입이 아니라면 바로 더할 수 없다. 따라서 더 넓은 범위를 가지는 타입으로 형 변환을 한 후 연산을 수행해야 한다. SomeType(ofInitialValue)는 Swift type의 initializer를 호출하여 초기값을 넘겨주는 기본적인 방법이다.

     

    Unit16Unit8 값을 받아들이는 initializer를 가지고 있기 때문에 아래와 같은 코드는 정상적으로 작동한다. 아무 타입으로나 가능한 것이 아니라 이전 타입에 대한 initializer를 제공하는 타입에 대해서만 가능하다. 새로운 타입의 값을 accept하도록 initializer를 추가하려면 Extension을 이용해야 한다.

    let twoThousand: UInt16 = 2_000
    let one: UInt8 = 1
    let twoThousandAndOne = twoThousand + UInt16(one)

     

    위 예시에서 one을 변환하지 않고 더하면 에러가 발생한다.

    Integer and Floating-Point Conversion

    정수 리터럴이 아닌 타입이 정해진 변수/상수를 더하는 경우 타입 변환을 반드시 해야 한다. three의 경우 타입이 Int로 정해진 상수이기 때문에 소수와 더하기 위해서는 타입 변환이 필요하다.

    let three = 3
    let pointOneFourOneFiveNine = 0.14159
    let pi = three + pointOneFourOneFiveNine // error
    
    let pi = Double(three) + pointOneFourOneFiveNine // 3.14159

     

    note

    정수 리터럴을 더하는 경우는 괜찮다. 3이라는 정수 리터럴 자체는 explicit한 타입을 가지고 있지 않기 때문이다. 타입은 변수/상수에 값을 할당하면서 정해지는 것이다.

    let pi = 3 + pointOneFourOneFiveNine // 3.14159

     

    소수에서 정수로의 변환은 당연히 explicit하게 해야 한다. Int의 경우 Double이나 Float 값에 대한 initializer가 있기 때문에 변환이 가능하다. 변환을 하면서 소수점은 잘라낸다. 예를 들어 -3.9는 -3이 되고, 4.5는 4가 된다.

    let integerPi = Int(pi) // 3

     

    Type Aliases

    typealias 키워드를 사용하면 기존 타입의 다른 이름을 정의할 수 있다.

    typealias AudioSample = UInt16
    var maxAmplitudeFound = AudioSample.min // 0

     

    Booleans

    Swift는 논리값을 표현하기 위해 기본적으로 Bool이라는 타입이 있다.

    let orangesAreOrange = true
    let turnipsAreDelicious = false

     

    Bool값은 주로 조건문에 사용된다.

    if turnipsAreDelicious {
        print("Mmm, tasty turnips!")
    } else {
        print("Eww, turnips are horrible.")
    }

    Swift의 type safety로 인해 Bool이 아닌 값이 Bool 값으로 교체되는 것은 불가능하다. type safety가 적용되는 이유는 accidental한 에러를 피하고 코드의 의도를 명확하게 하기 위해서이다.

    let i = 1
    if i {
        // this example will not compile, and will report an error
    }
    
    if i == 1 {
        // this example will compile successfully
    }

     

    Tuples

    여러 값들을 하나로 grouping하기 위해 사용한다. 튜플 안의 각 값은 어느 타입이라도 가능하고, 타입이 서로 달라도 된다.

    let http404Error = (404, "Not Found")
    // http404Error is of type (Int, String)
    
    let a = (1, 1.0, "sss") // (Int, Double, String)

     

    튜플의 element들을 상수/변수들로 decompose 하는 것도 가능하다.

    let (statusCode, statusMessage) = http404Error
    
    print("The status code is \(statusCode)")
    // Prints "The status code is 404"
    
    print("The status message is \(statusMessage)")
    // Prints "The status message is Not Found"

     

    튜플의 element들 중 일부만 필요한 경우, 필요없는 부분은 밑줄(_)로 처리하면 된다.

    let (justTheStatusCode, _) = http404Error
    print("The status code is \(justTheStatusCode)")
    // Prints "The status code is 404"

     

    각 element를 index를 통해 접근하는 것도 가능하다.

    print("The status code is \(http404Error.0)")
    // Prints "The status code is 404"
    
    print("The status message is \(http404Error.1)")
    // Prints "The status message is Not Found"

     

    각 element에 이름을 부여하면 index 뿐만 아니라 이름으로도 접근이 가능하다.

    let http200Status = (statusCode: 200, description: "OK")
    
    print("The status code is \(http200Status.statusCode)")
    // Prints "The status code is 200"
    
    print("The status code is \(http200Status.0)")
    // Prints "The status code is 200"

     

    튜플은 함수의 리턴값으로 유용하게 사용할 수 있다. 여러 타입을 가진 여러 값을 반환함으로써 outcome에 대한 더 유용한 정보를 제공할 수 있다. 튜플은 간단한 값들의 group로는 적절하지만, 사용하고자 하는 data structure가 더 복잡한 경우에는 class나 structure가 더 유용하다.

     

    Optionals

    필요성

    다음 코드를 살펴보자. String을 Int로 변환하려는 경우, String 값 안에 숫자가 아닌 값이 있어서 실패할 가능성도 있다. 따라서 아무 값도 가지지 않을 수 있는 타입이 필요할 것이다. 이를 위해 도입한 개념이 바로 Optional이다. 기존 타입 뒤에 '?'를 붙이면 Optional한 타입이 되어 아무 값도 가지지 않을 가능성이 존재하게 된다.

    let possibleNumber = "123"
    let convertedNumber = Int(possibleNumber) // 타입이 Int? 혹은 optional Int

     

    nil

    Optional한 상수/변수의 값이 없는 상태를 나타내기 위해 사용한다. Optional이 아닌 상수/변수에는 사용할 수 없다.

    var serverResponseCode: Int? = 404
    // serverResponseCode contains an actual Int value of 404
    
    serverResponseCode = nil
    // serverResponseCode now contains no value

     

    Optional 변수에 초기값을 할당하지 않으면 자동으로 nil로 설정된다.

    var surveyAnswer: String?
    // automatically set to nil
    note

    상수/변수가 특정 조건에서 값이 없는 경우를 포함해야 한다면, 항상 Optional로 선언하여 사용하자.

     

    If Statements & Forced Unwrapping

    Optional 타입의 값을 그대로 출력하려고 하면 Optional(값)으로 출력된다.

    let a: Int? = 100
    print(a) // Optional(100)

     

    따라서 값이 nil인지 검사 후 Optional 이름 뒤에 !을 붙여서 값을 얻어야 한다.

    if a != nil {
        print(a!) // 100
    }

     

    Optional Binding

    Optional이 값을 포함하고 있는지 확인하여 포함하고 있다면 해당 값을 임시로 사용할 수 있는 상수/변수로 만드는 것이다. if 혹은 while과 같이 사용할 수 있다.

    let possibleNumber = "123"
    
    if let actualNumber = Int(possibleNumber) {
        print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
    } else {
        print("The string \"\(possibleNumber)\" couldn't be converted to an integer")
    }
    // Prints "The string "123" has an integer value of 123"

     

    여러 Optional Binding을 콤마(,)로 구분하여 사용할 수도 있다. 이 중 하나라도 nil이거나 조건들 중 하나가 거짓이면, if문 전체 조건이 false로 간주된다.

    if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
        print("\(firstNumber) < \(secondNumber) < 100")
    }
    // Prints "4 < 42 < 100"
    
    if let firstNumber = Int("4") {
        if let secondNumber = Int("42") {
            if firstNumber < secondNumber && secondNumber < 100 {
                print("\(firstNumber) < \(secondNumber) < 100")
            }
        }
    }
    // Prints "4 < 42 < 100"

     

    note

    optional binding으로 만들어진 상수/변수의 경우 if문의 body에서만 사용이 가능하다. else나 else if에서는 불가능하다.

    Implicitly Unwrapped Optionals

    프로그램 구조상 Optional의 값이 한 번 할당되면 반드시 값을 가지는 것이 확실한 경우가 존재한다. 이 경우 사용하는 것이 implicitly unwrapped optional로, ? 대신 !을 사용하면 된다. 그러면 나중의 상수/변수의 값에 접근할 때 !를 사용할 필요가 없다. 주로 클래스를 초기화할 때 사용한다.

    let possibleString: String? = "An optional string."
    let forcedString: String = possibleString! // requires an exclamation point
    
    let assumedString: String! = "An implicitly unwrapped optional string."
    let implicitString: String = assumedString // no need for an exclamation point

     

    implicitly unwrapped optional을 사용하는 경우, Swift는 먼저 그냥 optional로 사용하려고 한다. optional로 사용할 수 없는 경우, 강제로 unwrapping을 한다.

    let a: Int! = 100
    var b: Int
    
    b = a
    print(b) // 100
    
    var c: Int? = a
    print(c) // Optional(100)

     

    optional binding에도 사용할 수 있다.

    if let d = a {
        print(b) // 100
    }

     

    Error Handling

    함수 등이 failure한 원인을 결정할 수 있게 하기 위한 기능이다. 또한 에러를 프로그램의 다른 파트로 넘길 수 있게 한다. 함수에서는 throws 키워드를 사용하면 함수 내부에서 에러를 던질 수 있다. 함수가 특정 조건에서 에러를 throw하면, 함수를 호출한 곳에서 catch를 하여 대응할 수 있다. Swift는 에러를 자동으로 현재 스코프 밖으로 전파하여, catch로 처리될 때까지 전파한다.

    func canThrowAnError() throws {
        // this function may or may not throw an error
    }

     

    do...catch문이 다른 언어의 try...catch문과 같은 역할을 한다.

    do {
        try canThrowAnError()
        // no error was thrown
    } catch {
        // an error was thrown
    }

     

    자세한 내용은 추후 포스팅에서 다룰 것이다.

     

    Assertions & Preconditions

    둘 다 런타임 시 조건을 확인하여 조건을 만족하지 못하면 프로그램이 종료된다. Assertion은 디버그 빌드에서만 체크하고, Precondition은 디버그, 프로덕션 빌드 모두 체크한다. 프로덕션 빌드에서는 Assertion이 체크되지 않기 때문에 프로덕션 성능에 영향을 주지 않으면서 Assertion을 사용할 수 있다.

    Assertions

    assert()를 호출하면, 첫번째 매개변수로 들어간 Bool값이 false이면 두번째 매개변수로 들어간 문자열이 메시지로 출력된다.

    let age = -3
    assert(age >= 0, "A person's age can't be less than zero.") // 프로그램 종료

     

    조건이 이미 체크된 경우 assertionFailure()를 호출하여 실패를 알릴 수 있다.

    if age > 10 {
        print("You can ride the roller-coaster or the ferris wheel.")
    } else if age >= 0 {
        print("You can ride the ferris wheel.")
    } else {
        assertionFailure("A person's age can't be less than zero.")
    }

    Preconditions

    assertion과 사용법이 같다.

     

    note

    unchecked mode로 컴파일하는 경우 precondition은 체크되지 않는다. 컴파일러가 precondition은 모두 참으로 가정하고 코드를 최적화하기 때문이다. 그러나 fatalError() 함수의 경우 최적화 설정에 상관없이 항상 실행을 멈추게 한다. 따라서 프로토타이핑이나 초기 개발 단계에서 fatalError()를 사용함으로써 치명적인 오류를 방지할 수 있다.

     

    Reference

    https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html

    'iOS > Swift 공식문서' 카테고리의 다른 글

    Functions  (0) 2022.02.15
    Control Flow  (0) 2022.02.15
    Collection Types  (0) 2022.02.14
    Strings & Characters  (0) 2022.02.14
    Basic Operators  (0) 2022.02.12

    댓글

Designed by Tistory.