Optional也是Objective-C沒有的數據類型,是蘋果引入到Swift語 言中的全新類型,它的特色就和它的名字同樣:能夠有值,也能夠沒有值,當它沒有值時,就是nil。此外,Swift的nil也和Objective-C有 些不同,在Objective-C中,只有對象才能爲nil,而在Swift裏,當基礎類型(整形、浮點、布爾等)沒有值時,也是nil,而不是一個初 始值,沒有初始值的值,是不能使用的,這就產生了Optional類型。定義一個Optional的值很容易,只須要在類型後面加上問號(?)就好了, 如:spa
var str: String?
一個Optional值和非Optional值的區別就在於:Optional值未經初始化雖然爲nil,但普通變量連nil都沒有:.net
//未被初始化,可是是一個Optional類型,爲nil var str: String? str //輸出nil //未被初始化,也不是Optional類型 var str2: String str2 //使用時出錯
Optional類型的值不能被直接使用,當須要用時要顯式拆包,以代表我知道這個Optional是必定有值的:code
var str: String? = "Hello World!" str! //Hello World!
對比拆包先後,對str的輸出:對象
var str: String? = "Hello World!" str //{Some "Hello World!"} str! //Hello World!
之因此要拆包使用,是由於Optional類型實際上是一個枚舉: ci
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裏面的值取出來。get
有沒有似曾相識的感受?Java裏面也有泛型。 |
除了顯式拆包,Optional還提供了隱式拆包,經過在聲明時的數據類型後面加一個感嘆號(!)來實現:編譯器
var str: String! = "Hello World!" str //Hello World!
可 以看到沒有使用(?)進行顯式的折包也獲得了Some中的值,這個語法至關於告訴編譯器:在咱們使用Optional值前,這個Optional值就會被 初始化,而且老是會有值,因此當咱們使用時,編譯器就幫我作了一次拆包。若是你確信你的變量能保證被正確初始化,那就能夠這麼作,不然仍是不要嘗試爲好。it
另外:在上面能夠看到,Optional其實就是一個枚舉,而後給它指定一個類型就好了,因此下面這兩種方法都能聲明一個Optional值:io
var str: String! = "Hello World!" var str2: Optional<String>
在說Optional Binding以前,我想先說下Xcode6 Beta5在這一版中的一個小變化:在Xcode6 Beta5以前,若是是一個Optional值,能夠直接放到條件判斷語句中,如:編譯
var str: String? = "Hello World!" if str { "not nil" } else { "nil" }
若是不是nil,則右邊的Playground會顯示「not nil」;反之則顯示「nil」,可是至Xcode6 Beta5開始,這樣就不能經過編譯器了,你須要用下面這種方式來代替:
var str: String? = "Hello World!" if str != nil { "not nil" } else { "nil" }
看似合理,可是在某種狀況下會很是不爽,好比你在str != nil條件成真後接着在上下文中使用str,會被要求進行拆包,咱們以一個Int類型的Optional來作示例:
var count: Int? count = 100 if count != nil { "count is " + String(count!) //count is 100 } else { "nil" }
我在把count強轉成String的時候被要求拆包了,這是由於count自己是一個Optional的類型,爲了不在條件判斷語句後執行一次或更屢次的拆包,Swift引進了Optional Binding,咱們就能夠這樣作:
var count: Int? count = 100 if let validCount = count { "count is " + String(validCount) //count is 100 } else { "nil" }
經過在條件判斷語句中(如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 { var name: String init (name: String) { self.name = name } }
一 個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的話,寫出來的就像這樣:
let jackon = Person() jackon.pet = Pet(name: "Max") jackon.pet?.favoriteToy = Toy(name: "Ball") 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,能使咱們的代碼變得更加可讀,同時更加簡潔。