《Advanced Swift》第九章 協議:讀書筆記 一、概述 二、協議目擊者 三、條件化協議實現 四、協議繼承 五、協議和關聯類型 六、存在體 七、類型消除器

一、概述

  • 協議可以自行擴展新的功能。最簡單的例子就是 Equatable,它要求實現的類型提供 == 操作 符。然後,它會根據 == 的實現提供 != 操作符的功能。類似的,Sequence 協議要求的方法並 不多 (它只要求提供一個產生迭代器的方法),但它卻可以通過擴展,爲自己加入大量可供使用 的方法。
  • 協議可以通過條件化擴展 (conditional extensions) 添加需要額外約束的 API。例如,在 Collection 協議中,只有 Element 實現了 Comparable 的時候,才提供了 max() 方法。
  • 協議可以繼承其它協議。例如,Hashable 要求實現的類型必須同時實現 Equatable 協議。類 似的,RangeReplaceableCollection 繼承自 Collection,而 Collection 繼承自 Sequence。換 句話說,我們可以構建一個協議層次結構。
  • 另外,協議還可以被組合起來形成新的協議。例如,標準庫中的 Codable 就是 Encodable 和 Decodable 協議組合之後的別名。
  • 有時,某個協議的實現還依賴於其它協議的實現。例如,當且僅當數組中 Element 類型實現了 Equatable 的時候,對應的數組類型才實現了 Equatable。這叫做條件化實現 (conditional conformance):Array 實現 Equatable 的條件,就是 Element 實現了 Equatable。
  • 協議還可以聲明關聯類型,實現了這個協議的類型就需要定義關聯類型對應的具體類型。例如, IteratorProtocol 定義了一個關聯類型 Element,每一個實現了 IteratorProtocol 的類型就都 要定義自己的 Element 類型。

上面提到的這些協議特性並不限於標準庫,我們也可以用它們創建自己的協議。儘管面向協議 編程在 Swift 中不可或缺,但我們還是要先潑盆冷水。每一個協議都會引入一層額外的抽象, 有時,這會增加理解代碼的難度。但有時,使用協議又可以極大地簡化代碼。這需要不斷在編 碼中積累經驗,才能 (在複雜度和表意上) 找到平衡。

二、協議目擊者

// 數組元素沒有實現Equatable協議時,寫一個判斷數組元素是否相等時,可以採用這個方法
extension Array {
  func allEqual(_ compare: (Element, Element) -> Bool) -> Bool {
    guard let f = first else { return true }
    for el in dropFirst() {
      guard compare(f, el) else { return false }
    }
    return true
  }
}

struct Eq<A> {
  let eq: (A, A) -> Bool
}

// 類似這樣的,就叫做顯示目擊者
let eqInt: Eq<Int> = Eq { $0 == $1 }

extension Array {
  func allEqual(_ compare: Eq<Element>) -> Bool {
    guard let f = first else { return true }
    for el in dropFirst() {
      guard compare.eq(f, el) else { return false }
    }
  return true
  }
}

三、條件化協議實現

extension Array: Equatable where Element: Equatable {
  static func ==(lhs: [Element], rhs: [Element]) -> Bool {
    fatalError("Implementation left out")
  }
}

四、協議繼承

public protocol Comparable : Equatable {
  static func < (lhs: Self, rhs: Self) -> Bool // ...
}

五、協議和關聯類型

// 關聯類型是協議實現泛型的一種手段
protocol IteratorProtocol {
  associatedtype Element
  mutating func next() -> Element?
}

六、存在體

嚴格來說,在 Swift 中是不能把協議當作一個具體類型來使用的,它們只能用來約束泛型參數。當我們把協議當作具體類型使用的時候,編譯器會爲協議創建一個包裝類型,叫做存在體 (existential)。爲協議創建的這個盒子也叫做 存在體容器 (existential container)。這是編譯器必須要做的事 情,因爲它需要在編譯期確認類型的大小。不同的類型自身大小有差異 (例如:所有的類都是一 個指針的大小,而結構體和枚舉的大小則依賴它們的實際內容),這些類型實現了一個協議的時 候,把協議包裝在存在體容器中可以讓類型的尺寸保持固定,編譯器也就能確定對象的內存布 局了。
在 Swift 5 裏,存在體只針對那些沒有關聯類型和 Self 約束的協議。

七、類型消除器

儘管我們無法爲帶有 Self 或關聯類型約束的協議創建存在體,但我們可以編寫一個執行類似功 能的函數,叫做:類型消除器 (type erasers)。
具體內容見:Swift與泛型編程第四彈:類型擦除

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章