12일차 - 21.06.19

수킴·2021년 6월 19일
0

100DaysOfSwift

목록 보기
13/37
post-thumbnail

학습키워드

  • optionals
  • unwrapping
  • typecasting

1. Handling missing data

값을 저장하려는 경우 정확한 값을 알지 못했을 때 Int 라면 0을 String 이라면 "" 을 사용할 수 있지만 값이 없다는 것과는 다른 의미입니다.

스위프트에서는 값이 없다는 것을 표현하기 위해 옵셔널을 사용합니다.

  • 옵셔널은 열거형으로 구현되어 있습니다.
  • 값이 없다는 뜻은 nil 을 사용합니다.
var age: Int? = nil // nil

age = 38 // 38
  • 처음에 값이 없다는 것을 정의한 후 변경할 수 있습니다.

스위프트에서는 왜 옵셔널이 있는 걸까?

옵셔널은 스위프트의 모든 타입에 사용할 수 있습니다.

옵셔널을 사용하여 값이 없다는 것을 나타낼 수 있습니다. 컴파일러에게 값의 정보를 알려줄 수 있어서 컴파일 중에 최대한 많은 버그를 잡아낼 수 있습니다.

2. Unwrapping optionals

변수나 상수를 옵셔널타입으로 선언하면 값에 접근할 때 nil인상태에서 접근하면 에러가 발생할 것입니다. 따라서 이러한 처리를 하기위해 언래핑을 사용합니다.

옵셔널타입만 언래핑을 사용합니다.

언래핑을 하는 방법 중 하나는 if let 구문을 사용합니다

if let 값이 있는 경우 동작할 코드 else 값이 없는 경우(nil) 동작할 코드

var name: String? = nil

if let unwrapped = name {
    print("\(unwrapped.count) letters")
} else {
    print("Missing name.")
}

옵셔널값을 사용하면 언래핑을 사용해야 하는 이유?

변수나 상수를 옵셔널타입으로 정의하면 값들은 옵셔널로 래핑되어서 저장됩니다.

스위프트는 옵셔널값을 언래핑하지 않고서는 사용할 수 없게 만들었습니다. 예를들어 값이 없는경우인데 그 값을 활용하는 경우 여러 곳에서 에러가 발생할 수 있기 때문입니다.)

3. Unwrapping with guard

옵셔널을 언래핑하는 또 다른 방법은 guard let 구문이 있습니다.

값이 nil 이라면 현재 함수, 루프 또는 조건을 종료합니다.

if letguard let 의 가장 큰 차이점은 해제한 시점에서 블록 내부에서 작동하는지 블록외부에서 작동할 수 있는 지 여부입니다.

func greet(_ name: String?) {
    guard let unwrapped = name else {
        print("You didn't provide a name!")
        return
    }

    print("Hello, \(unwrapped)!")
}

🌟 if letguard let 의 속도차이가 있을까 ? → 없다.

4. Force unwrapping

값이 nil 이 아닌 것을 확신하는 경우에 옵셔널을 강제로 언래핑을 할 수 있습니다.

사용방법은 ! 기호를 붙여줍니다.

let str: String = "5"
let num = Int(str)!

5. Implicitly unwrapped optionals

암묵적으로 언래핑된 옵셔널은 값을 포함하거나 nil을 가질 수 있습니다. 그러나 일반 옵셔널과 달리 래핑을 풀 필요가 없습니다.

옵셔널을 해제하지 않고 nil을 저장하여도 코드가 충돌하지 않습니다. 하지만 nil인 경우 접근하면 코드가 충돌합니다.

var age: Int! = 4 // 4

age = 5 // 5
age = nil // nil

6. Nil coalescing

nil병합연산자는 옵셔널을 풀고 값이 있으면 내부 값을 반환하고 nil이라면 기본값을 반환합니다.

사용방법은 옵셔널 값 뒤에 ?? 문자를 추가한후 기본값을 설정합니다.

func username(for id: Int) -> String? {
    if id == 1 {
        return "Taylor Swift"
    } else {
        return nil
    }
}

let user: String = username(for: 15) ?? "Anonymous"  // Anonymous
let user1: String = username(for: 1) ?? "Anonymous" // Taylor Swift
  • nil병합연산자를 사용하여 옵셔널을 언래핑해줄 수 있습니다.

7. Optional chaining

강제 언래핑을 대체로써 옵셔널 체이닝을 사용합니다. 강제언래핑은 값이 없는 경우 런타임에러가 발생하지만 옵셔널체이닝을 사용하면 런타임에러 대신 nil 이 반환됩니다.

옵셔널체이닝 사용방법은 ? 을 붙여줍니다.

// MARK: - names배열에 first가 있는 경우
let names = ["John", "Paul", "George", "Ringo"]

let beatle = names.first?.uppercased() // "JOHN"

// MARK: - names배열에 first가 없는 경우
let names: [String] = []

let beatle = names.first?.uppercased() // nil
  • a.b.c 에 접근하는 경우 b가 옵셔널일 때, 그 뒤에 ? 를 써서 옵셔널체인을 활성화할 수 있습니다. a.b?.c
  • b값이 nil인 경우 나머지 라인은 모두 nil로 반환

옵셔널체이닝이 중요한 이유

옵셔널체이닝을 사용하면 한줄의 코드에서 여러 옵션계층을 파헤칠 수 있으며, 이러한 계층 중 하나라도 nil이라면 전체 줄이 nil이 됩니다.

let names = ["Vincent": "van Gogh", "Pablo": "Picasso", "Claude": "Monet"]
let surnameLetter = names["Vincent"]?.first?.uppercased()

8. Optional try

에러처리를 하는 구문 do ~ catch , try 구문이 있는데 try?을 사용하면 던지는 함수를 옵셔널을 반환하는 함수로 변경합니다. 함수에서 오류가 발생하면 nil로 반환하고, 그렇지 않으면 반환값이 옵셔널래핑된 값을 반환합니다.

enum PasswordError: Error {
    case obvious
}

func checkPassword(_ password: String) throws -> Bool {
    if password == "password" {
        throw PasswordError.obvious
    }

    return true
}

// MARK: - try 사용한 경우
do {
    try checkPassword("password")
    print("That password is good!")
} catch {
    print("You can't use that password.")
}

// MARK: - try? 사용한 경우
if let result = try? checkPassword("password") {
    print("Result was \(result)")
} else {
    print("D'oh.")
}

// 실행결과
D' oh
  • password 문자열입력하면 throw 하므로 try? nil인경우 else블록으로 이동합니다.
  • try! : 함수가 실패하지 않는다는 것을 확실할 때 사용합니다.

9. Failable initializers

이니셜라이저를 사용할 때 작동하거나 작동하지 않을 수 있는 실패할 수 있는 이니셜라이저가 있습니다.

이니셜라이저 시점에 객체생성에 대한 에러를 미리 처리할 수 있습니다.

사용방법은 init?() 을 사용합니다. nil 이라면 반환할 수 있습니다.

struct Person {
    var id: String

    init?(id: String) {
        if id.count == 9 {
            self.id = id
        } else {
            return nil
        }
    }
}

let person = Person(id: "123456")
let person1 = Person(id: "123456789")

print(person?.id)
print(person1?.id)

// 실행결과
nil
Optional("123456789")

10. Typecasting

  • 업캐스팅 : 하위클래스 → 상위 클래스로 캐스팅 (오류가 발생하지 않습니다.)
  • 다운캐스팅 : 상위클래스 → 하위 클래스로 캐스팅 (오류가 발생할 수 있습니다.)
    1. 옵셔널 캐스팅 sender as? UISwitch 결과값은 옵셔널타입
    2. 강제 캐스팅 sender as! UISwitch 결과값은 일반타입
class Animal { }
class Fish: Animal { }

class Dog: Animal {
    func makeNoise() {
        print("Woof!")
    }
}

let pets: [Animal] = [Fish(), Dog(), Fish(), Dog()]

for pet in pets {
    if let dog = pet as? Dog {
        dog.makeNoise()
    }
}
  • [Animal] 배열 중에 makeNoise()메서드를 사용할 수 있는 Dog타입으로 다운캐스팅 된 경우만 메서드실행됩니다.

링크

100 Days of Swift - Day 12 - Hacking with Swift

profile
iOS 공부 중 🧑🏻‍💻

0개의 댓글