Skip to content

[week01] Optional

Daehoon Lee edited this page May 7, 2023 · 4 revisions

1주차 토요스터디 / 주제: Optional / (23.04.29)

🎁 Optional Unwrapping

  • If Statements and Forced Unwrapping 옵셔널의 실제 값에 상관없이 강제로 값을 추출하는 방법이다. 옵셔널이 nil일 때 강제추출을 사용하면 런타임 에러가 발생하여 이런 에러를 피하기 위해 if문을 사용해 먼저 nil인지 확인하고 강제추출 방법을 사용한다.

    if convertedNumber != nil {
        print("convertedNumber has an integer value of \(convertedNumber!).")
    }
  • Optional Binding

    안전하게 옵셔널 값을 언래핑 하는 방법으로 가장 많이 사용된다. if문과 guard문의 가장 큰 차이점은 선언한 임시 상수에 대한 사용 범위(scope)의 차이가 있다.

    if letguard let 비교

    • if let
      • 옵셔널 값 있으면 if구문에서 처리하고, nil이면 else에서 처리한다.
      • 임시 상수의 사용 범위는 if문 안에서만 사용 가능하다.
    • guard let
      • 옵셔널 값이 있으면 다음 코드를 실행하고 아니라면 else문을 실행한다.
      • 임시 상수의 사용 범위는 같은 코드 블록 범위이다.
    if let <#constantName#> = <#someOptional#> {
       <#statements#>
    }
    
    while let <#constantName#> = <#someOptional#> {
       <#statements#>
    }
    
    guard let name = person["name"] else {
            return
    }
  • Implicitly Unwrapped Optionals

    강제 추출이나 옵셔널 바인딩처럼, 별도의 추출 과정을 거치지 않아도 자동으로 옵셔널이 해제되는 방법이다.

    let assumedString: String! = "An implicitly unwrapped optional string."
    let implicitString: String = assumedString // no need for an exclamation point
  • Optional Chaining

    .을 통해 내부 프로퍼티나 메서드에 연속적으로 접근할 때, 표현식의 옵셔널 값이 nil이면 전체 표현식의 값도 nil이 된다.

    class Person {
        var name: String
        var job: Job?
        
        init(name: String) {
            self.name = name
        }
    }
    
    class Job {
        var jobName: String
        var pay: Int
        
        init(jobName: String) {
            self.jobName = jobName
            self.pay = 0
        }
    }
    
    let hoon: Person? = Person(name: "hoon")
    
    hoon?.job?.pay = 400000      // hoon.job은 nil이므로 실행되지 않음
    print(hoon?.job?.pay)        // -> nil
    
    hoon?.job = Job(jobName: "developer")
    hoon?.job?.pay = 400000
    print(hoon?.job?.pay)        // -> Optional(400000)
  • nil-coalescing operator

    옵셔널 값이 nil이 아니면 옵셔널을 언래핑한 값을, nil이면 ?? 뒤의 기본값을 사용한다.

    var text:String?
    var output = text ?? "Default value"
    print(output) // Default value
    
    text = "This is a string"
    output = text ?? "Default String"
    print(output) // This is a string

🔎 실험 1

예제 코드
// Optional
var productsList: [String?] = ["볼펜", "텀블러", "다이어리", "에코백", "머그컵", "후드집업"]

// !
for (index, product) in productsList.enumerated() {
    if product != nil {
        print("\(index)번 상품은 \(product!)입니다.")
    }
}

// Optional binding: if let
for (index, product) in productsList.enumerated() {
    if let product {
        print("\(index)번 상품은 \(product)입니다.")
    }
}

// Optional binding: guard
for (index, product) in productsList.enumerated() {
    guard let product else { continue }
    print("\(index)번 상품은 \(product)입니다.")
}

// Optional pattern matching
for (index, product) in productsList.enumerated() {
    if case let name? = product {
        print("\(index)번 상품은 \(name)입니다.")
    }
}

🔎 실험 2

예제 코드
  • throws - throw를 사용한 예외처리
enum productError: Error {
    case empty(Int)
    case poor
}

var budget: Int = 2000
var productsList: [String?] = ["볼펜", "텀블러", "다이어리", "에코백", "머그컵", "후드집업"]

//do-catch
func buy(productNumber: Int) throws {
    guard let product = productsList[productNumber] else { throw productError.empty(productNumber) }
    guard budget >= 1000 else { throw productError.poor }
    
    budget -= 1000
    print("\(productsList[productNumber]!) 구매, 현재 잔고는 \(budget)")
    productsList[productNumber] = nil
}

for index in [1, 2, 2, 3, 5] {
    do {
        try buy(productNumber: index)
    } catch productError.empty(let index) {
        print("\(index)번 상품이 없습니다.")
    } catch productError.poor {
        print("돈이 부족합니다.")
    }
}
예제 코드
  • Result Type을 사용한 예회처리
enum productError: Error {
    case empty(Int)
    case poor
}

var budget: Int = 2000
var productsList: [String?] = ["볼펜", "텀블러", "다이어리", "에코백", "머그컵", "후드집업"]

//Result Type
func buy2(productNumber: Int) -> Result<String, productError> {
    guard let product = productsList[productNumber] else { return .failure(.empty(productNumber)) }
    guard budget >= 1000 else { return .failure(.poor) }
    
    let productName: String = productsList[productNumber]!
    
    budget -= 1000
    productsList[productNumber] = nil
    
    return .success(productName)
}

for index in [1, 2, 2, 3, 5] {
    let a = buy2(productNumber: index)
    
    switch a {
    case .success(let name):
        print("\(name) 구매")
    case .failure(let error):
        print(error)
//    case .failure(let error(let x))
//        print("aaaa", error)
    }
}

📚 참조링크