天天看點

Swift 可選值(Optional Values)介紹

Optional也是Objective-C沒有的資料類型,是蘋果引入到Swift語言中的全新類型,它的特點就和它的名字一樣:可以有值,也可以沒有值,當它沒有值時,就是nil。此外,Swift的nil也和Objective-C有些不一樣,在Objective-C中,隻有對象才能為nil,而在Swift裡,當基礎類型(整形、浮點、布爾等)沒有值時,也是nil,而不是一個初始值,沒有初始值的值,是不能使用的,這就産生了Optional類型。定義一個Optional的值很容易,隻需要在類型後面加上問号(?)就行了,如:

var str: String?

一個Optional值和非Optional值的差別就在于:Optional值未經初始化雖然為nil,但普通變量連nil都沒有:

//未被初始化,但是是一個Optional類型,為nil

str //輸出nil

//未被初始化,也不是Optional類型

var str2: String

str2    //使用時出錯

Optional類型的值不能被直接使用,當需要用時要顯式拆包,以表明我知道這個Optional是一定有值的:

var str: String? = "Hello World!"

str! //Hello World!

對比拆包前後,對str的輸出:

str     //{Some "Hello World!"}

str!    //Hello World!

之是以要拆包使用,是因為Optional類型其實是一個枚舉: 

enum Optional<T> : Reflectable, NilLiteralConvertible {

    case None

    case Some(T)

    init()

    init(_ some: T)

    /// Haskell's fmap, which was mis-named

    func map<U>(f: (T) -> U) -> U?

    func getMirror() -> MirrorType

    static func convertFromNilLiteral() -> T?

}

當Optional沒有值時,傳回的nil其實就是Optional.None,即沒有值。除了None以外,還有一個Some,當有值時就是被Some<T>包裝的真正的值,是以我們拆包的動作其實就是将Some裡面的值取出來。

有沒有似曾相識的感覺?Java裡面也有泛型。

除了顯式拆包,Optional還提供了隐式拆包,通過在聲明時的資料類型後面加一個感歎号(!)來實作:

var str: String! = "Hello World!"

str //Hello World!

可以看到沒有使用(?)進行顯式的折包也得到了Some中的值,這個文法相當于告訴編譯器:在我們使用Optional值前,這個Optional值就會被初始化,并且總是會有值,是以當我們使用時,編譯器就幫我做了一次拆包。如果你确信你的變量能保證被正确初始化,那就可以這麼做,否則還是不要嘗試為好。

另外:在上面可以看到,Optional其實就是一個枚舉,然後給它指定一個類型就行了,是以下面這兩種方法都能聲明一個Optional值:

var str2: Optional<String>

在說Optional Binding之前,我想先說下Xcode6 Beta5在這一版中的一個小變化:在Xcode6 Beta5之前,如果是一個Optional值,可以直接放到條件判斷語句中,如:

if str {

    "not nil"

} else {

    "nil"

如果不是nil,則右邊的Playground會顯示“not nil”;反之則顯示“nil”,但是至Xcode6 Beta5開始,這樣就不能通過編譯器了,你需要用下面這種方式來代替:

if str != nil {

看似合理,但是在某種情況下會非常不爽,比如你在str != nil條件成真後接着在上下文中使用str,會被要求進行拆包,我們以一個Int類型的Optional來做示例:

var count: Int?

count = 100

if count != nil {

    "count is " + String(count!)    //count is 100

我在把count強轉成String的時候被要求拆包了,這是因為count本身是一個Optional的類型,為了避免在條件判斷語句後執行一次或更多次的拆包,Swift引進了Optional Binding,我們就可以這樣做:

if let validCount = count {

    "count is " + String(validCount)    //count is 100

通過在條件判斷語句中(如if、while等)把Optional值直接給一個臨時常量,Swift會自動檢測Optional是否包含值,如果包含值,會隐式的拆包并給那個臨時常量,在接下來的上下文中就能直接使用這個臨時常量了,這樣是不是就覺得很爽呢

注:在Optional Binding中,除了以常量的方式去接收拆包的值之外,也能以一個變量的形式

去接收,但相信在大多數情況下我們隻是使用那個值就行了,并不會去改變它。

Swift 1.2 新文法:

在if let 中可以使用條件判斷了: 

var a: NSString?

a = "test"

if let b = a {

    b

if true, let b = a where b == "test" {

    "true"

如果a 不是"test",則不會列印出"true"。

Optional Chaining對Swift來說是很基本但又必不可少的東西,相對于簡單類型(Int、String等)來說,Optional更主要的應用場景是在複雜對象上,當一個對象包含另一個對象,同時這兩個對象都有可能為nil的情況下才是Optional派上用場的地方,在Objective-C裡,向nil發消息得到的就是一個nil,但是Swift不能在nil上直接調用方法或屬性,同時為了友善我們使用,進而引入了Optional類型,可是這還不夠,我們做一個簡單的例子:

class Person {

    var pet: Pet?

class Pet {

    var name: String

    var favoriteToy: Toy?

    init (name: String) {

        self.name = name

    }

class Toy {

一個Person對象代表一個人,這個人可能有一個寵物,寵物會有它自己的名字,而且寵物可能會有自己喜愛的玩具,按照前面提到的知識,我們要首先判斷這個人有沒有寵物,然後再判斷他的寵物有沒有喜愛的玩具,然後才能得到這個玩具的名稱,利用Optional Binding,我們寫出來的可能就像這樣:

let jackon = Person()

jackon.pet = Pet(name: "Max")

jackon.pet?.favoriteToy = Toy(name: "Ball")

if let pet = jackon.pet {

    if let toy = pet.favoriteToy {

        toy.name

這裡用到了兩個if,因為pet和toy對象都可能為nil,我們需要預防每一個可能為nil的對象,如果這個對象再複雜一點,那if也就更多了,而使用Optional Chaining的話,寫出來的就像這樣:

if let toy = jackon.pet?.favoriteToy {

    toy.name

當一個Optional值調用它的另一個Optional值的時候,Optional Chaining就形成了,基本上,Optional Chaining就是總是傳回一個Optional的值,隻要這個Chaining中有一個值為nil,整條Chaining就為nil,和Objective-C的向nil發消息類似。

有一點很有趣,就是Optional Chaining除了能将屬性傳回的類型變為Optional外,連方法的傳回值都能強制變為Optional,哪怕這個方法沒有傳回值,但是别忘了,Void也算是一個類型:

typealias Void = ()

如果我們的Pet類有一個玩玩具的play方法的話,就可以這樣來判斷是否會調用成功:

if let p: Void = jackon.pet?.play() {

    "play is called"

使用Optional Chaining,能使我們的代碼變得更加可讀,同時更加簡潔。