Swift中 Nil Coalescing 運算符的使用技巧
在Swift官方《The Swift Programming Language》文檔 Beta 5 版本中的 Basic Operators 一節(jié)中添加了Nil Coalescing Operator小結(jié),介紹了一個新的運算符Nil Coalescing,符號是 ?? ,它的表現(xiàn)形成如下:
- let c = a ?? b
這個運算符有兩個條件:
a. 必須是Optional類型的。
b. 的類型必須要和a解包后的值類型一致。
符合這兩個條件后,我們來解釋一下上述這行代碼,意思就 是c 的值是 a 或 b 中一個的值,但有前提條件,就是當 a 解包后值不為 nil 時,那么就將 a 解包后的值賦值給 c,如果 a 解包后值為 nil,那么就將 b 的值賦值給c。
我們還可以用三目運算來更形象的解釋這個運算符:
- let c = a != nil ? a! : b
從上面的代碼我們很容易理解,當a的值不等于nil時,將a解包后的值賦值給c,否則將b的值賦值給c。
我們來看看官方給的代碼示例:
- let defaultColorName = "red"
- var userDefinedColorName: String? // 默認值為nil
- var colorNameToUse = userDefinedColorName ?? defaultColorName
- // 因為userDefinedColorName的值是nil,所以colorNameToUse的值為"red"
- userDefinedColorName = "green"
- colorNameToUse = userDefinedColorName ?? defaultColorName
- // 因為userDefinedColorName的值不為nil,所以colorNameToUse的值為"green"
大家看到這應(yīng)該對 Nil Coalescing 這個運算符有比較清晰的理解了,但在實際運用中,我們還需要注意以下幾點。
編譯器中的類型匹配原則
原諒我在末尾加的 ; ,編碼習(xí)慣而已 = =||
我們先來看一段代碼:
- let a: Int? = nil;
- let b: Int? = 7;
- let c = a != nil ? a! : b; // 因為a的默認值為nil,所以c的值為{Some 7}
- let d = a ?? b; // 這里d的值為nil,這是怎么回事?
首先我們需要注意的是,在官方文檔中有這么一句話:“The expression b must match the type that is stored inside a”。但是上面的代碼示例中,我們的 b 是 Int? 類型,那么此時我們的編譯器會怎么處理呢?我們通過自己實現(xiàn)一個 Nil Coalescing 運算符來說明,代碼片段如下:
- infix operator ??? {
- associativity right;
- precedence 110;
- }
- func ???<T>(a: T?, b: @autoclosure () -> T) -> T {
- return a != nil ? a! : b();
- }
- let d = a ??? b; // 這里d的值仍然是nil
從上面的代碼片段中我們可以看出,a的類型是根據(jù)傳入的b的類型決定的,所以當我們傳入的 b 是 Int? 類型時,編譯器其實將 a 的類型自動轉(zhuǎn)換為 Int?? 類型了,也就是 Optional(a) ,那么我們就能解釋 let d = a ?? b; 這行代碼。因為 a == nil 但是 Optional(a) != nil,所以 d = Optional(a)! ,d 的值為 nil 。
Nil Coalescing 運算符返回值的類型
我們先看示例代碼片段:
- let a: Int? = nil;
- let b: Int? = 5;
- let c: Int? = 6;
- // 因為a的默認值為nil,所以將b的值賦值給x,x的值為{Some 5},類型為Int?
- let x = a != nil ? a! : b;
- // 因為c的值不為nil,所以將c解包后的值賦值給y,但是y的值卻是{Some 6},而不是6,這是怎么回事?
- let y = c != nil ? c! : b;
根據(jù)上面我們提到的編譯器中類型判斷的原則就可以理解了,因為b的類型是Int?,所以在編譯時 c 的類型已經(jīng)成了 Int?? ,所以為 Int?? 解包的類型就是 Int? 了。
如果我們聲明變量 y 的類型,編譯器就要提出抗議了:
- // 如何a為Int?類型,那么編譯器就不會通過,因為會導(dǎo)致賦值運算符兩邊類型不等
- let x: Int = a != nil ? a! : b;
- // 這種寫法和上面一樣,都不會編譯通過
- let y: Int = a ?? b;