# Swift 可選值(Optional Values)介紹
## Optional的定義
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
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 :?Reflectable,?NilLiteralConvertible?{
? ??case?None
? ??case?Some(T)
? ??init()
? ??init(_?some:?T)
? ??/// Haskell's fmap, which was mis-named
? ??func?map(f: (T) ->?U) ->?U?
? ??func?getMirror() ->?MirrorType
? ??static?func?convertFromNilLiteral() ->?T?
}
~~~
當Optional沒有值時,返回的nil其實就是Optional.None,即沒有值。除了None以外,還有一個Some,當有值時就是被Some包裝的真正的值,所以我們拆包的動作其實就是將Some里面的值取出來。
有沒有似曾相識的感覺?Java里面也有泛型。
### 隱式拆包
除了顯式拆包,Optional還提供了隱式拆包,通過在聲明時的數據類型后面加一個感嘆號(!)來實現:
~~~
var?str:?String! =?"Hello World!"
str?//Hello World!
~~~
可以看到沒有使用(?)進行顯式的折包也得到了Some中的值,這個語法相當于告訴編譯器:在我們使用Optional值前,這個Optional值就會被初始化,并且總是會有值,所以當我們使用時,編譯器就幫我做了一次拆包。如果你確信你的變量能保證被正確初始化,那就可以這么做,否則還是不要嘗試為好。
**另外:**在上面可以看到,Optional其實就是一個枚舉,然后給它指定一個類型就行了,所以下面這兩種方法都能聲明一個Optional值:
~~~
var?str:?String! =?"Hello World!"
var?str2:?OptionalString>
~~~
## 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 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
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,能使我們的代碼變得更加可讀,同時更加簡潔。
- 前言
- iOS 自定義頁面的切換動畫與交互動畫 By Swift
- Swift 元組(Tuples)介紹
- Swift 可選值(Optional Values)介紹
- Swift Switch介紹
- Swift 值類型和引用類型
- Swift 柯里化(Currying)
- iOS GCD使用指南
- iOS8 Core Image In Swift:自動改善圖像以及內置濾鏡的使用
- 讓Xcode自動更新Build版本
- Swift 全功能的繪圖板開發
- Swift Nullability and Objective-C
- Swift Core Data 圖片存儲與讀取Demo
- Swift 繪圖板功能完善以及終極優化
- 如何設計一個 iOS 控件?(iOS 控件完全解析)