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,能使我們的代碼變得更加可讀,同時更加簡潔。