2017年9月25日

Optional in swift

在物件導向程式語言中,如果一開始沒有定義一個變數本身儲存的資料,通常會用 nil(null) 來先定義變數,然後在後面要使用到這個變數時,進行物件的初始化。

在沒有 Optional 的狀況下,程式必須要在使用該變數時,先檢查這個變數倒底初始化了沒,如果沒有初始化,還得跳過使用這個變數的程式區塊,或是直接在這裡初始化,古早以前的 Java 程式就常常遇到這樣的問題,在使用到沒有初始化的變數時,發生 Null Pointer Exception,而造成程式 crash。

Optional 的宣告

swift 的 Optional 是以 enum 的方式實作,Optional 所代表的意義為 "there is a value, and it equals x" 或是 "there isn’t a value at all"

Optional 的定義宣告如下,可得知有兩種case: 有值 Some 及 無值 None

public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
    case None
    case Some(Wrapped)
}

Optional 就像是一個物件的包裝盒/包裹,裡面裝了 Some 或是 None 兩種東西,Some 就是盒子裡裝了其他類型的物件 instance,None 就是盒子裡面沒有東西。

在 swift 要可以用 Optional.some 或是 Optional.none 包裝物件 instance。

let box = Optional.some("iphone")
let box2:Optional<String> = Optional.some("iphone")
let box3:Optional<String> = Optional.none

print("box3 is \(box3)")

? 是 swift 裡面針對 Optioanl 的 syntax sugar,在宣告時,型別後面加上?,就可以宣告該變數為 Optional

var newbox:String?
newbox = nil
print("newbox is \(newbox)")

let newbox2:String? = "iphone"
let newbox3:String? = nil

使用 Optional 裡面的物件

Optional 是個包裝盒,要使用 optional 變數必須要用下面幾種方式,將盒子拆開

  • 使用 !

要使用 Optional 可用下面 unwrap 函數取得裡面的物件,但在 box 為 nil 時,會發生 crash 的狀況

func unwrap<String>(box:Optional<String>) -> String {
    switch box {
    case .none:
        fatalError("it is Nil !!!")
    case let .some(boxval):
        return boxval
    }
}

let tempbox1 = unwrap(box: "iphone")
//let tempbox2 = unwrap(box: nil)

在 swift 可用 ! 這個 syntax sugar 語法,其功能就跟 unwrap 一樣,遇到 nil 時,程式會 crash。

let tempbox3 = newbox2!
//let tempbox4 = newbox3!
  • if

先用 if 判斷一下 Optional 裡面有沒有東西,如果有就打開使用,如果沒有就不處理

if newbox2 != nil {
    print ("newbox2 is \(newbox2!)")
}

if newbox3 != nil {
    print ("newbox3 is \(newbox3!)")
}
  • if let

可簡化第二種方式的實作,直接將盒子裡的物件指定給另一個變數

if let pbox2 = newbox2 {
    print("pbox2 is = \(newbox2!)")
} else {
    print("pbox2 is nil")
}

if let pbox3 = newbox3 {
    print("pbox3 is = \(newbox3!)")
} else {
    print("pbox3 is nil")
}
  • Assigned Value if nil

用另一個變數來儲存物件 instance,但在打開 Optional 時,如果發現是 nil,就將新的變數初始化為空白的字串

let pbox5: String
if let pbox4 = newbox2 {
    pbox5 = pbox4
} else {
    pbox5 = ""
}

Optional Chaining ?.

Optioanl Chaining ?. 是 swift 的另一個 syntax sugar

[object]?.[property]?.[property | method]?.[method]
class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

直接用 ! 打開 john 時,會造成程式 crash,但改用 ?.,可避免這樣的問題。

let john = Person()
//let roomCount = john.residence!.numberOfRooms
let roomCount = john.residence?.numberOfRooms

使用 ?. 可在打開 optional 時,進行 nil 的檢查及保護,搭配 if let 使用,就可以避免因為 nil 而造成程式 crash。

if let roomCount2 = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount2) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}

john.residence = Residence()

if let roomCount3 = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount3) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}

References

初學Swift:愛恨交織的 Optional

Swift的問號與驚嘆號:可有可無的 Optional

Swift Optional 變數的處理方法

[Swift] 第二章 - 可選(Optional)