Optional的定義
Optional是 OC 中沒有的數據類型,是蘋果在 Swift 中引入的全新類型,它的特點就是可有值,也可以沒有值,當它沒有值的時候就是 nil. 并且 Swift 中的nil 和 OC 中 nil 也不一樣,在 OC 中只有對象才能為 nil, 而在 Swift 中,當基礎類型(整型,浮點,布爾等)沒有值的時候,也是 nil, 而不是一個初始值,沒有初始值的值是不能使用的,所以就產生了 Optional 類型.定義一個 Optional 的值很容易,只需要在類型后面加上問號(?)就行了,例如:
var str: String?
let status: Int? = 1 // 申明可選Int類型的常量,初始值為1
var defaultAddress: String? = "江蘇南京" // 申明可選String類型的變量,初始值為"江蘇南京"
var student: Person? // 申明可選Person(自定義的類)的變量,初始值為nil
注意:
Int?和 Int 不相同, Int? 代表可選的 Int 類型,可以賦值為 nil, 而 Int 不可以賦值為 nil
一個 Optional 值和非 Optional 值的區別就在于: Optional 值未經初始化雖然為 nil, 但普通變量未經初始化連 nil 都沒有
//未被初始化,但是是一個Optional類型,為nil
var str: String?
str //輸出nil
//未被初始化,也不是Optional類型
var str2: String
str2 //使用時出錯
Optional 的拆包
顯式拆包
Optional 類型的值不能被直接使用,當需要用時要顯示拆包,以表明我知道這個 Optional 是一定有值的:
var str: String? = "Hello World!"
str! //Hello World!
對比拆包前后, str 的輸出
var str: String? = "Hello World!"
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 里面的值取出來
注意:
1.在拆包的時候,如果是網絡請求的參數中有可選值的時候,一定都要拆包,不然的話只要有一個Optional 類型的參數沒有進行解包,所有的參數都會變成 Optional 類型的
2.使用"!"強制解包獲取可選類型的值的時候容易造成程序崩潰(不建議直接使用)
var defaultAddress: String? = "上海"
if defaultAddress != nil { // !=或==可以用來判斷是否為nil
print("您的地址是\(defaultAddress!)") // 使用!強制解析
} else {
print("對不起,您不存在地址信息")
}
var student: Person?
print("學生為\(student!)") // XCode會提示運行錯誤,因為student初始值為nil,強制解析不行
隱式拆包
除了顯示拆包之外, Optional 還提供了隱式拆包,通過在聲明時的數據類型后面加一個感嘆號(!)來實現
var str: String! = "Hello World!"
str //Hello World!
可以看到沒有使用(?)進行顯式的折包也得到了Some中的值,這個語法相當于告訴編譯器:在我們使用Optional值前,這個Optional值就會被初始化,并且總是會有值,所以當我們使用時,編譯器就幫我做了一次拆包。如果你確信你的變量能保證被正確初始化,那就可以這么做,否則還是不要嘗試為好。
另外:在上面可以看到,Optional其實就是一個枚舉,然后給它指定一個類型就行了,所以下面這兩種方法都能聲明一個Optional值:
var str: String! = "Hello World!"
var str2: Optional<String>
空運算符
可以用來判斷變量或常量是否為nil
// 空合運算符:a ?? b 判斷a是否為nil,若a不為nil對a解包,否則返回b的值
var status: Int? // 申明可選Int類型的變量status,初始值為nil
status ?? 0 // 因為status為nil,則返回0
// ?? 即為以下if else的縮寫
func testOption() -> Int {
let status: Int? = 1
if status == nil {
return 0
} else {
return status!
}
}
Optional Binding
在說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 Chaining
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,能使我們的代碼變得更加可讀,同時更加簡潔。
在我總結 Swift 的時候發現一篇簡書寫的也不錯簡書