Swift 4.2 新特性詳解 Conditional Conformance 的更新

隨着 Xcode 10 的正式版發佈,Swift 4.2 也正式問世,在 Swift 4.1 中引入的 Conditional Conformance 也有了一個小的升級,使用便利性再次提升。不瞭解 Swift 4.1 的同學也沒有關係,本篇文章會針對 Conditional Conformance 進行完整的梳理。

首先我們對 Conditional Conformance 下個定義:

Conditional conformances express the notion that a generic type will conform to a particular protocol only when its type arguments meet certain requirements.

Conditional Conformance 表達的含義是:當一個泛型類型的類型參數滿足某些條件時,該泛型類型實現了某個特定協議。舉個例子,當 Array 類型的類型參數是 Equatable 的時候,我們希望 Array 自動成爲 Equatable,具體以 extension 語法表達,如下:

extension Array: Equatable where Element: Equatable {
  static func == (lhs: Array,  rhs: Array) -> Bool {
    guard lhs.count == rhs.count else {
      return false
    }
    for (i, v) in lhs.enumerated() {
      if rhs[i] != v {
        return false
      }
    }
    return true
  }
}

這個擴展不僅可以用來直接比較一維數組,由於可以遞歸推斷,同樣可以比較多維數組:下面的 a1 和 a2 都是 Array<Array<Int>> 類型 ,由於 Int 是 Equatable ,所以 Array<Int> 也是 Equatable ,因此 Array<Array<Int>> 是Equatable,可以直接比較是否相等。

let a1 = [[123], [456,789]]
let a2 = [[123], [456,789]]
let a3: [[Int]] = []
print(a1 == a2) // true
print(a1 == a3) // false

關於 Conditional Conformance 還有一個非常重要的特性,就是它對於運行時 Conditional Conformance 的支持。來看下面的代碼:

protocol P {
  func doSomething()
}

struct S: P {
  func doSomething() { print("S") }
}

extension Array: P where Element: P {
  func doSomething() {
    for value in self {
      value.doSomething()
    }
  }
}

// compile-time
func doSomethingStatically<E: P>(_ value:Array<E>) {
  value.doSomething()
}

// runtime
func doSomethingIfP(_ value: Any) {
  if let p = value as? P {
    p.doSomething()
  } 
}

doSomethingIfP([S(), S(), S()]) 

我們看到了兩個版本,前者是編譯器確定了這個擴展的有效性;後者是 runtime 的時候做檢查,它體現了 Conditional Conformance 對動態檢查的支持。

如何來直觀感受 Conditional Conformance 運行時特性的作用呢?我們可以想象是自己是被傳入的參數:我是個普通的 Array,只不過我的元素類型實現了 P。結果可以動態發現我作爲 Array 也實現了 protocol P,並且擁有了新方法 doSomething 。哪怕我是被作爲 Any 類型傳入的,動態也能判斷上述事實。我只是個 Array ,元素類型實現了 P 而已。 Conditional Conformance 的擴展使這一切成爲可能。

這裏我們回顧一個知識點,爲什麼我們最後需要新寫個 protocol P 來舉例,而不直接用前面的 Equatable 呢?

用另一個問題可以回答:在Swift中,可不可以寫 as? Equatable,或者 var e : Equatable = 10 呢?其實是不能的,因爲Equatable這樣的protocol只能作爲泛型的類型約束,而不能作爲可以直接hold值的類型,原因是它有 associated type 或者Self;也沒有Equatable<Int> 的寫法,Equatable 不是泛型類型,只是泛型約束。

事實上,從 Swift 4.1 開始標準庫就加入了一系列 Equatable、Encodable、Decodable 的 Conditional Conformance,例如:

extension Optional: Equatable where Wrapped: Equatable { /* ... */ }

extension Array: Equatable where Element: Equatable { /* ... */ }

extension Dictionary: Equatable where Value: Equatable { /* ... */ }

extension Array: Encodable where Element: Encodable { /* ... */ }

extension Array: Decodable where Element: Decodable { /* ... */ }

Swift 4.2 中對 Hashable 也內置了一系列 Conditional Conformance:也就是當其類型參數是 Hashable 的時候 Optional, Array, Dictionary 和 Range 也是 Hashable,非常便利的一個改進。

小結

  • 介紹了 Conditional Conformance 的語法和語義
  • 以 Array 爲例介紹了 Conditional Conformance 用途
  • Conditional Conformance 支持運行時判斷
  • 標準庫內置 Array 和 Hashable 的 Conditional Conformance
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章