swift基礎(chǔ)-optional

轉(zhuǎn)載鏈接swift-optional
本是學(xué)習(xí)控制流時(shí)引入下optional,本來打算自己寫寫,發(fā)現(xiàn)下面作者寫的很好,很全,因?yàn)橄胧珍浀絪wift專題下方便大家學(xué)習(xí),只好搬運(yùn)過來了,再次感謝作者轉(zhuǎn)載鏈接swift-optional
Optional的定義
Optional也是Objective-C沒有的數(shù)據(jù)類型,是蘋果引入到Swift語言中的全新類型,它的特點(diǎn)就和它的名字一樣:可以有值,也可以沒有值,當(dāng)它沒有值時(shí),就是nil。此外,Swift的nil也和Objective-C有些不一樣,在Objective-C中,只有對(duì)象才能為nil,而在Swift里,當(dāng)基礎(chǔ)類型(整形、浮點(diǎn)、布爾等)沒有值時(shí),也是nil,而不是一個(gè)初始值,沒有初始值的值,是不能使用的,這就產(chǎn)生了Optional類型。定義一個(gè)Optional的值很容易,只需要在類型后面加上問號(hào)(?)就行了,如:

var str: String?        // 沒有值得str輸出為nil

一個(gè)Optional值和非Optional值的區(qū)別就在于:Optional值未經(jīng)初始化雖然為nil,但普通變量連nil都沒有:

//未被初始化,但是是一個(gè)Optional類型,為nil
var str: String?
str //輸出nil
//未被初始化,也不是Optional類型
var str2: String
str2    //使用時(shí)出錯(cuò)

Optional的拆包
顯式拆包
Optional類型的值不能被直接使用,當(dāng)需要用時(shí)要顯式拆包,以表明我知道這個(gè)Optional是一定有值的:

var str: String? = "Hello World!"
str! //Hello World!

對(duì)比拆包前后,對(duì)str的輸出:

var str: String? = "Hello World!"
str     //{Some "Hello World!"}
str!    //Hello World!

之所以要拆包使用,是因?yàn)镺ptional類型其實(shí)是一個(gè)枚舉:

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?
}

當(dāng)Optional沒有值時(shí),返回的nil其實(shí)就是Optional.None,即沒有值。除了None以外,還有一個(gè)Some,當(dāng)有值時(shí)就是被Some<T>包裝的真正的值,所以我們拆包的動(dòng)作其實(shí)就是將Some里面的值取出來。
有沒有似曾相識(shí)的感覺?Java里面也有泛型。

隱式拆包
除了顯式拆包,Optional還提供了隱式拆包,通過在聲明時(shí)的數(shù)據(jù)類型后面加一個(gè)感嘆號(hào)(!)來實(shí)現(xiàn):

var str: String! = "Hello World!"
str //Hello World!

可以看到?jīng)]有使用(?)進(jìn)行顯式的折包也得到了Some中的值,這個(gè)語法相當(dāng)于告訴編譯器:在我們使用Optional值前,這個(gè)Optional值就會(huì)被初始化,并且總是會(huì)有值,所以當(dāng)我們使用時(shí),編譯器就幫我做了一次拆包。如果你確信你的變量能保證被正確初始化,那就可以這么做,否則還是不要嘗試為好。
另外:在上面可以看到,Optional其實(shí)就是一個(gè)枚舉,然后給它指定一個(gè)類型就行了,所以下面這兩種方法都能聲明一個(gè)Optional值:

var str: String! = "Hello World!"
var str2: Optional<String>

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

var str: String? = "Hello World!"
if str {
    "not nil"
} else {
    "nil"
}

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

var str: String? = "Hello World!"
if str != nil {
    "not nil"
} else {
    "nil"
}

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

var count: Int?
count = 100
if count != nil {
    "count is " + String(count!)    //count is 100
} else {
    "nil"
}

我在把count強(qiáng)轉(zhuǎn)成String的時(shí)候被要求拆包了,這是因?yàn)閏ount本身是一個(gè)Optional的類型,為了避免在條件判斷語句后執(zhí)行一次或更多次的拆包,Swift引進(jìn)了Optional Binding,我們就可以這樣做:

var count: Int?
count = 100
if let validCount = count {
    "count is " + String(validCount)    //count is 100
} else {
    "nil"
}

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

注:在Optional Binding中,除了以常量的方式去接收拆包的值之外,也能以一個(gè)變量的形式
去接收,但相信在大多數(shù)情況下我們只是使用那個(gè)值就行了,并不會(huì)去改變它。

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",則不會(huì)打印出"true"。

Optional Chaining
Optional Chaining對(duì)Swift來說是很基本但又必不可少的東西,相對(duì)于簡單類型(Int、String等)來說,Optional更主要的應(yīng)用場景是在復(fù)雜對(duì)象上,當(dāng)一個(gè)對(duì)象包含另一個(gè)對(duì)象,同時(shí)這兩個(gè)對(duì)象都有可能為nil的情況下才是Optional派上用場的地方,在Objective-C里,向nil發(fā)消息得到的就是一個(gè)nil,但是Swift不能在nil上直接調(diào)用方法或?qū)傩裕瑫r(shí)為了方便我們使用,從而引入了Optional類型,可是這還不夠,我們做一個(gè)簡單的例子:

class Person {
    var pet: Pet?
}

class Pet {
    var name: String
    
    var favoriteToy: Toy?
    
    init (name: String) {
        self.name = name
    }
}

class Toy {
    var name: String
    
    init (name: String) {
        self.name = name
    }
}

一個(gè)Person對(duì)象代表一個(gè)人,這個(gè)人可能有一個(gè)寵物,寵物會(huì)有它自己的名字,而且寵物可能會(huì)有自己喜愛的玩具,按照前面提到的知識(shí),我們要首先判斷這個(gè)人有沒有寵物,然后再判斷他的寵物有沒有喜愛的玩具,然后才能得到這個(gè)玩具的名稱,利用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
    }
}

這里用到了兩個(gè)if,因?yàn)閜et和toy對(duì)象都可能為nil,我們需要預(yù)防每一個(gè)可能為nil的對(duì)象,如果這個(gè)對(duì)象再復(fù)雜一點(diǎn),那if也就更多了,而使用Optional Chaining的話,寫出來的就像這樣:

let jackon = Person()
jackon.pet = Pet(name: "Max")
jackon.pet?.favoriteToy = Toy(name: "Ball")
if let toy = jackon.pet?.favoriteToy {
    toy.name
}

當(dāng)一個(gè)Optional值調(diào)用它的另一個(gè)Optional值的時(shí)候,Optional Chaining就形成了,基本上,Optional Chaining就是總是返回一個(gè)Optional的值,只要這個(gè)Chaining中有一個(gè)值為nil,整條Chaining就為nil,和Objective-C的向nil發(fā)消息類似。
有一點(diǎn)很有趣,就是Optional Chaining除了能將屬性返回的類型變?yōu)镺ptional外,連方法的返回值都能強(qiáng)制變?yōu)镺ptional,哪怕這個(gè)方法沒有返回值,但是別忘了,Void也算是一個(gè)類型:
typealias Void = ()
如果我們的Pet類有一個(gè)玩玩具的play方法的話,就可以這樣來判斷是否會(huì)調(diào)用成功:

if let p: Void = jackon.pet?.play() {
    "play is called"
}

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容