[Swift] Optional Type 그리고 ?

Optional의 존재는 nil 로부터 온다.

 nil 의미

Swift 에서의 nil 은 Objective-C 에서의 nil 과 다르다.

  • nil of Swift : 값이 없다  in 모든타입 (Reference or Value Type)
  • nil of Objective-C : 값이 없다 in Reference Type

즉, Swift에선 어떤 타입이든 값이 없으면  nil 이다.

Optional Type 

Optional Type 은 nil이 될 수도  있는 변수 (or 상수)를 의미하며 선언할 때 ‘?’ 를 사용한다.  ‘?’  이 없으면 Non-Optional Type 으로 nil이 될 수 없다.

// optional type.
var optionalString : String?   // nil 로 초기화

// non-optional type.
var nonOptionalString : String // nil 이 될 수 없음. 초기화 필요

WWDC 2014 에서 Safe, Power, Modern 세가지를 Swift의 특징으로 꼽았다. Optional은  Safe에 속한다.

기존 nil 로 인한 앱 크래쉬 문제를  nil 전용 변수 타입을 따로 정의해서 compile 시점에서 막아줄께 하는 것이다. (실제로 이것이 얼마나 안전한지는 더 써봐야 알겠다.)

개발자는 변수 선언할 때마다 Optional Type으로 선언할 것인지 판단해야 한다. 꼭 필요한 경우에만 Optional Type 사용을 권장한다.

Optional은 enum 으로 정의된다.

enum Optional<T>{
    case None
    case Some(T)
}

! (forced-unwrapping operator)

Optional을 이용하여 안전한 코드를 짜려면 반드시 if 문으로 값이 있는지 확인을 해야 한다. 값이 있는 경우엔 ‘!’ 를 이용하여 값이 있음을 명시적으로 알려줘야 한다.

‘!’ 는 forced-unwrapping operator 로서 애매모호한 Optional Type 값을 명확하게 들어낸다. 이를 통해 불필요한 Optional Type 추가 발생을 막는다.

let numCards = ["one" : 1 , "two" : 2, "three" : 3,
               "four" : 4 , "five": 5]

// dictionary[key] 는 optional type을 반환 (즉,myCard 는 Int?)
let myCard = numCards["two"]
if myCard { // 값을 체크

    let card = myCard! // 값이 있으므로 !로 값을 드러냄
    // card의 타입은 Int (Int? 가 아님)
   
    println("card \(card)")

}else {

    println("no card")
}

Runtime Error

Optional Type을 if 문 없이 unwrapping 하여 사용하면 Runtime Error가 발생한다. 더 이상 안전하지 않다.

let numCards = ["one" : 1 , "two" : 2, "three" : 3, "four" : 4 , "five": 5]
let myCard = numCards["nine"]    // myCard = nil 

println("\(myCard!)")      // Runtime Error 발생

 Optional Binding

결국 Optional Type을 다룰때는 항상

  1. 값 여부를 확인 하고
  2. 값이 있으면 unwrapping 하여

사용해야 한다.

그러나 매번 위처럼 단계적으로 처리하는 건 불편하다. Optional Binding은 이를 동시에 할 수 있다.

Optional Binding 포멧은 if let ~ 이다.

let numCards = ["one" : 1 , "two" : 2, "three" : 3,
               "four" : 4 , "five": 5]

if let card = numCards["two"] { // 값 체크 & unwapping 

    println("card \(card)")  // card 는 Int 타입.

}else{
    println("no card")
}

Optional Chaining

코드를 짜다보면 Optional Binding 이 중첩될 수 있다. 반복되는  if let~ 은  귀찮다.

여러 층의 Optional Binding을 한 줄에 쓰게 하는 것이 Optional Chaining이다. (Swift 개발팀에서도 Optional이 불편하긴 했나보다.)

Optional Chaining은 ‘?’ 기호를 사용한다. (Optional Type 선언에 사용하는 ‘?’ 과 동일시 하면 헷갈린다. 비슷하지만 다른것으로 보자.)

아래 예제 코드는 WWDC2014 에서 소개된 코드다.

// 중첩 Optional Binding
if let home = paul.residence {
    if let postalAddress = home.address{
        if let building = postalAddress.buildingNumber{
            if let convertedNumber = building.toInt(){
                println("building number of paul is \(convertedNumber)")
            }
         }
     }
}

// Optional Chaining
if let addressNumber = paul.residence?.address?.buildingNumber?.toInt(){

    println("building number of paul is \(addressNumber)")</pre>
}

코드를 간단히 설명하면 paul 은 class 개체로 Non-Optional Type 이고 나머지 residence, address, buildingNumber 는 Optional Type 이다.

paul 의 집 주소를 알기 위해 Optioanl Binding으로 순차적으로 값을 확인한다. 이를 Optional Chaining 을 이용하면 한 줄에 끝이다.

Optional Chaining을 철도 선로로 이해하면 쉽다. 값이 없으면 nil 행이고 값이 있으면 keep going. (WWDC2014 비디오 스크린샷을 올리기가 좀 그렇지만, 좋은 설명이라 일단 첨부한다.)

< Optional Chaining from WWDC2014 >

 

Optional 과 Enum Pattern Matching

앞서 살펴봤듯이 Optional 은 enum 타입이다.

enum Optional<T>{
    case None
    case Some(T)
}

따라서 enum을 이용한 Pattern Matching 이 가능하다.

let numberCards : Dictionary<String,Int> = [ "one" : 1, "two" : 2, "three": 3, "four" : 4,
                  "five" : 5, "six": 6, "seven" : 7]

for num in numberCards.keys{

    // Dictionary[key] 리턴은 optional type
    switch numberCards[num]{

        case .Some(let v) where v > 3 :
            println("\(v)")
        default:
          println("...")

    }

}

numberCards[num] 의 리턴값은 Optional Type 이므로 – 여기선 Int? – 위와 같이 switch, enum 을 이용하여 처리할 수 있다.

 

* Reference

Leave a comment

9 Comments

  1. 글 잘봤습니다. 감사합니다.

    Reply
  2. AllanCho

     /  October 14, 2014

    좋은글 덕분에 배우고 갑니다 ^^

    Reply
  3. Hello

     /  May 8, 2015

    최근 스위프트 보고 있는데 많은 도움 되었습니다^^!

    Reply
  4. Thanks!

     /  August 10, 2015

    Optional 이 왜 필요하지?
    이런 궁금증을 가지고 있었는데 알려주셔서 감사합니다!

    Reply
  5. 중간에 예제가 틀린 부분이 있습니다.

    if myCard {

    이 아니라

    if (myCard != nil) {

    이어야 하네요.

    Reply
    • 네. 맞습니다. Swift 가 업데이트되면서 Optional type 의 불리언 정책이 바뀌었습니다. 제가 업데이트를 하지 못한 부분인데 상기시켜 주셔서 감사합니다. (조만간 최신 버전의 swift 를 기준으로 재정리할 예정에 있습니다.)

      Reply

Leave a reply to Hello Cancel reply