swift 中的 ??

Inlight發表於2017-12-21

引言

最近有幸參加公司的iOS招聘面試,發現很多3年左右工作經驗的工程師對 swift 還停留在想要去了解的階段,一些是由於公司專案的原因,一些是因為個人原因,但 swift 4.0 都已經出了,還在抱著觀望的心態實在不像一個做技術的人應有的態度,所以最近想寫一些跟 swift 相關的知識點,既作為自己對知識點的整理和儲存,也希望能幫助到一些正在學習 swift 的小夥伴~~~

概念

空合運算子,當然這個運算子並不是 swift 首創,早在 C#,Perl,PHP7.0.0 等均有此運算子。

作用

這是一個非常有用而且常用的操作符,可以用來快速對 nil 進行條件判空,使程式碼看起來更加簡潔。

  • 使用之前
// 寫法一:可選繫結(Optional Binding)
var username = ""
if let name = inputName {
    username = name
} else {
    username = "Guest"
}

// 寫法二:三目運算子(ternary operator)
let username = inputName != nil ? inputName! : "Guest"

複製程式碼
  • 使用之後
let username = inputName ?? "Guest" 
複製程式碼

事實上 a ?? b 表示將對可選型別a進行為空判斷,如果a包含一個值,就進行解封,否則就返回一個預設值b。 注意:表示式 a 必須是 Optional 型別。預設值 b 的型別必須要和 a 儲存值的型別保持一致。

定義

public func ??<T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
public func ??<T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
複製程式碼
  • @autoclosure

@autoclosure作用就是把一句表示式自動地封裝成一個閉包,@autoclosure並不支援帶有輸入引數的寫法,只能使用類似 () -> T 的引數才能使用這個特性。所以寫接受 @autoclosure 的方法時還需謹慎,在容易產生誤解的時候,還是建議使用完整的閉包。

瞭解了 @autoclosure 之後 我們來猜一下 ?? 的實現

  • 實現
public func ??<T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T {
    switch optional {
        case .Some(let value):
            return value
        case .None:
            return defaultValue()
    }
}
複製程式碼

到這裡我想除了 ?? 的實現之外,對 Optional 的實現應該也有所瞭解了

enum Optional<T> : Reflectable, NilLiteralConvertible {
    case None
    case Some(T)
    init()
    init(_ some: T)
    ...
}
複製程式碼

當 Optional 沒有值時,返回的 nil 其實就是 Optional.None,即沒有值。除了 None 以外,還有一個 Some,當有值時就是被 Some(T) 包裝的真正的值,所以拆包其實就是將Some裡面的值取出來。

可能有些朋友會有疑問為什麼這裡要使用 @autoclosure,直接將 T 作為引數返回不行嗎?這正是 @autoclosure 一個最值得稱讚的地方。如果我們直接使用 T,那麼意味著 ?? 操作符真正取值之前,我們就必須準備好一個預設值,這個預設值的準備和計算會降低效能。但如果 optional 不是 nil,就完全不需要這個預設值,會直接返回 optional 解包後的值。這種情況下預設值的準備就屬於過度開銷了。使用 @autoclosure 就是將預設值的計算推遲到 optional 確定為 nil 之後。

相關文章