(對於可選類型的理解能夠參見貓神的這篇行走於 Swift 的世界中,在此整理一些關鍵的部分。)git
Swift 中的 Optinal Value 就像是一個盒子,可能裝着值,也可能什麼都沒裝。github
咱們能夠用 ?
定義一個可選類型的整數 :swift
var num: Int? num = nil //nil num = 3 //{Some 3}
可選類型的真實身份實際上是 Optional
類型,經常使用的 ?
是 Apple 提供的語法糖。使用 Optional
的寫法是這樣的:app
var num: Optional<Int> num = Optional<Int>() // nil num = Optional<Int>(3) // {Some 3}
點進去看下 Optional
的定義:ide
enum Optional<T> : Reflectable, NilLiteralConvertible { case None case Some(T) /// Construct a `nil` instance. init() /// Construct a non-\ `nil` instance that stores `some`. init(_ some: T) /// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`. func map<U>(f: (T) -> U) -> U? /// Returns a mirror that reflects `self`. func getMirror() -> MirrorType /// Create an instance initialized with `nil`. init(nilLiteral: ()) }
這樣一來,var num: Int? = 3
其實就是 Optional.Some(3)
。post
在這裏,?
聲明瞭一個 Optional<T>
類型的變量,而後作了判斷:.net
Optional.None
init(_ some: T)
初始化並返回 Optional.Some(T)
。在遭遇可選類型的時候,咱們能夠在 Optional 變量後面加上 !
進行強制解包。這樣雖然減小了代碼量,可是容易帶來隱患,使用的時候務必要慎重。就像是過馬路同樣,必定要仔細看好兩邊車輛,不然悲劇隨時可能發生。code
而實際上,不少時候能夠避免使用 !
,用其餘方法取而代之。排序
來看這樣一個例子:ci
let ages = [ "Tim": 53, "Angela": 54, "Craig": 44, "Jony": 47, "Chris": 37, "Michael": 34, ] let people = sorted(ages.keys, <).filter { ages[$0]! < 50 } println(people) // "[Chris, Craig, Jony, Michael]"
在這裏先對 ages
進行了排序,而後篩選出年紀小於 50 的人。由於是對字典取值,會出現 nil
,因此 ages[$0]
是 optional 的,須要進行解包。
固然也能夠用 if let
:
let people = sorted(ages.keys, <).filter { if let age = ages[$0] { return age < 50 } return false; }
可是原本一行就能搞定的問題卻拖沓到了五行才解決。實際上,換個思路,咱們徹底不須要遭遇可選類型:
let people = filter(ages) { $0.1 < 50 }.map { $0.0 }.sorted(<)
filter
中 $0 表示傳入的 key-value ,$0.1 表示 value,map
中 $0 表示傳入的 key-value ,$0.0 表示 key 。或許這樣可讀性比較差,能夠經過 tuple 包裝一下入參:
let people = filter(ages) { (k,v) in v < 50 }.map { (k,v) in k }.sorted(<)
在平時的開發中,咱們能夠用 if let
或者 ??
替換掉 !
。若是確實確實確定不會出問題再去用 !
。
一樣是 !
符號,若是放在類型後面,則表示隱式解析可選類型 (Implicitly Unwrapped Optionals):
var num: Int! num = 1 // 1 num = nil // nil
經過 !
定義的變量,實質上只是對 Optional 的變量多了一層封裝,幫咱們完成了本來須要手動解包的過程。
在什麼場合下可使用隱式解析可選類型呢?
有些尷尬的狀況,咱們想定義常量屬性,無奈它的初始化依賴於其餘屬性值。若是你用可選類型,實在是麻煩,由於你確信無疑它確定會在調用以前就完成初始化,不多是 nil
,這個時候你能夠用 !
進行定義。
舉個例子:
class MyView : UIView { @IBOutlet var button : UIButton var buttonOriginalWidth : CGFloat! override func viewDidLoad() { self.buttonOriginalWidth = self.button.frame.size.width } }
這部份內容在 Day11 中有涉及,其實更好的方法是用 lazy 延時加載,再也不贅述。
隱式解析可選類型彷佛是爲了照顧 objc 這個歷史包袱而存在。好比 UITableView
中:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? { return nil }
咱們很清楚的知道,在調用的時候 tableView
和 indexPath
不可能爲 nil
,若是還要 if let
這樣解包實在是浪費時間。若是是純粹的 Swift 語言寫的,絕對不會定義成 optional 類型。
除了上述的狀況2 ,其餘狀況都不應用 (包括狀況1)。