Swift集合類型協議淺析(上)

導 讀git

狐友技術團隊github

Swift是一門面向協議的語言,協議能夠被擴展,來給遵循該協議的類型提供方法等具體實現,經過擴展協議,咱們能夠爲協議所要求的方法提供默認實現。在Swift出現之前,協議在iOS中就十分重要,想一想UITableViewDataSource 和 UITableViewDelegate 等協議的討論,能夠說他們天天出如今咱們的腦海裏;使用Swift編程中必定會用到標準庫中的協議,例如Array就是一個繼承了10個協議的Struct,Bool類型是一個繼承了7個協議的Struct;算法

本篇文章爲Swift集合類型協議淺析系列文章的上篇,在這篇(上)中,咱們嘗試去解讀一些基礎協議的內部關係和邏輯,向你展現Swift如此強大的祕密。編程

Sequenceapi

Sequence是一組值的列表,可以提供對元素的順序、迭代訪問。markdown

1protocol Sequence {2   associatedtype Iterator3   func makeIterator() -> Iterator4}5protocol IteratorProtocol {6   associatedtype Element7   mutating func next() -> Element?8}
複製代碼

遵循Sequence協議須要有一個名爲makeIterator的獲取遍歷器的方法,返回遍歷器Iterator,Iterator遵循IteratorProtocol協議,協議有一個mutating的next方法,這個方法返回Sequence中下一個對象,直到沒有返回nil。閉包

舉個例子:dom

1struct InfiniteIterator: IteratorProtocol {2  let value: Int3  mutating func next() -> Int? {4    return value5  }6}
複製代碼

InfiniteIterator遵循IteratorProtocol協議,因此須要有next方法,這裏咱們簡化一下,讓next的返回值始終是value:ide

1var iterator = InfiniteIterator(value: 24)2iterator.next()   //243iterator.next()   //24
複製代碼

你會發現輸出始終是24,下面咱們繼續實現Sequence協議:函數

1struct InfiniteSequence: Sequence {2  let value: Int3  func makeIterator() -> InfiniteIterator {4    return InfiniteIterator(value: value)  //注意此處5  }6}
複製代碼

實現makeIterator方法,返回的類型是上一步剛剛實現的遵循IteratorProtocol的協議對象InfiniteIterator,這樣一個遵循Sequence協議的對象就構成了;咱們能夠嘗試使用Sequence協議的prefix方法,取前幾個對象測試:

1let infinite = InfiniteSequence(value: 20)2for value in infinite.prefix(5) {3  print(value)   //204}
複製代碼

遵循Sequence的類型均可以使用for in遍歷,如:

1let array = [1,2,3]2for item in array {3    print(item)4}
複製代碼

那麼爲何可以這麼使用呢?內部的實現相似下面,因爲有了迭代器和得到下一個元素的next方法,咱們就能夠知道下一個,下一個的下一個,不斷重複。

1var iterator = someSequence.makeIterator()2while let element = iterator.next() {3   doSomething(with:element)4}
複製代碼

簡單小結一下:

(1)Sequence能夠是有限序列,也能夠是無限序列,如同上面InfiniteSequence就是一個無限序列;

(2)Sequence只能夠迭代一次,有些時候能夠屢次進行迭代,可是不能保證每次均可以對其屢次迭代。

AnySequence

爲了簡化建立Sequence須要遵循協議的複雜性,咱們發現標準庫幫咱們提供了一個sequence方法:

1func sequence<T>(first: T, next: @escaping (T) -> T?) -> UnfoldFirstSequence<T>
複製代碼

這個函數有兩個參數,第一個參數須要Sequence序列返回的第一個值,第二個參數是一個閉包,接受以前的sequence元素並返回下一個:

1func infiniteBasic(value: Int) -> UnfoldSequence<Int, (Int?, Bool)> {2 return sequence(first: value) { _ in return value }3}
複製代碼

返回類型UnfoldSequence 遵循IteratorProtocol和Sequence,這樣會簡化上一步,就不須要寫兩個類分別遵循這兩個協議:

1for value in infiniteBasic(value: 24).prefix(5) {2  print(value)3}
複製代碼

輸出結果仍然是24,與前面的分別實現的效果一致;

而後咱們就來看看這小節的AnySequence,它是一個類型擦除器,官方這樣定義:

An instance of AnySequence forwards its operations to an underlying base sequence having the same Element type, hiding the specifics of the underlying sequence.

它本質上並無什麼做用,只是用來隱藏內部真實的類型,能夠類比OC類型中的id,有類似的做用。AnySequence遵循Sequence協議,因此上面的infiniteBasic能夠改造爲:

1func infinite(value: Int) -> AnySequence<Int> {2  return AnySequence {3    sequence(first: value) { _ in return value }4  }5}6for value in infinite(value: 24).prefix(5) {7  print(value)    //24  8}
複製代碼

AnyIterator

類型擦除序列。AnyIterator是AnySequence的實例,將其操做轉發給具備相同元素類型的底層基序列,從而隱藏底層序列的細節。實質是傳入一個生成下一個元素的閉包,內部經過next方法日後遍歷下一個Element元素類型。

1func infinite(value: Int) -> AnySequence<Int> {2  return AnySequence<Int> {3    AnyIterator<Int> { value }4  }5}
複製代碼

AnyIterator中閉包return下一個元素,其中很適合使用defer作索引的+1獲-1操做,這樣隱藏了IteratorProtocol的實現,好比下面的例子:

1var x=0 2func infinite2(value: Int) -> AnySequence<Int> { 3    return AnySequence<Int> { 4        AnyIterator<Int> { 5            defer { 6                x+=1 7            } 8            return x<15 ? x : nil 9        }10    }11}



1for value in infinite2(value: 24) {2    print(value)3}
複製代碼

這段代碼會每次不斷迭代+1返回,直到15爲止。

小結一下目前用到的這幾個類型的關係,Sequence協議有Iterator屬性,這個屬性遵循IteratorProtocol協議,UnfoldSequence協議同時遵循Sequence協議和IteratorProtocol協議,AnySequence遵循Sequence協議,AnySequence也有相同Iterator屬性,這個屬性遵循AnyIterator協議,而AnyIterator協議又同時遵循IteratorProtocol和Sequence,構成了一個關聯關係,因此咱們能夠經過AnySequence和AnyIterator的組合構成Sequence。

Collection

collection 是一個有索引的sequence,能夠從任何index反覆尋址不少次(單向)。

實現一個collection:

  • 定義Comaprable index type;

  • 定義 startIndex;

  • 定義 endIndex,是最後一個元素的下一個;

  • 定義方法 index(after:) 增長index;

  • 定義O(1) subscript operator get only 通關給定index,返回元素element。

咱們來舉一個具體的例子,稱爲Fizz Buzz Collection;咱們要建立一個範圍從1到100的集合,打印範圍1-100,被3整除時,print Fizz;被5整除時,print Buzz;若是同時可以被3和5整除,print FizzBuzz。

1struct FizzBuzz: Collection { 2 3    typealias Index = Int 4 5    var startIndex: Index { 6        return 1 7    } 8 9    var endIndex: Index {10        return 10111    }1213    func index(after i: Index) -> Index {14        return i + 115    }1617    func index(_ i: Int, offsetBy distance: Int) -> Int {18        return i + distance19    }2021    subscript (index: Index) -> String {22        precondition(indices.contains(index), "out of 1-100")23        switch (index.isMultiple(of: 3), index.isMultiple(of: 5)) {24        case (false, false):25            return String(index)26        case (true, false):27            return "Fizz"28        case (false, true):29            return "Buzz"30        case (true, true):31            return "FizzBuzz"32        }33    }34}
複製代碼

Collection繼承Sequence,與Sequence不一樣的是,Collection再也不多是無限的,你總能知道集合當中有多少個元素,所以咱們能夠對集合進行屢次迭代;而對序列而言,通常只能迭代一次;另外,協議中新增的主要元素就是名爲Index的新關聯類型,Collection的範圍經過屬性從startIndex到endIndex標記,須要注意的是,endIndex指向最後一個元素的下一個位置,因此是101;另外,Index這個關聯類型必須是Comparable,要得到下一個元素會增長index,直到抵達endIndex,這時候迭代會終止。

1for value in FizzBuzz() {2    print(value)3}
複製代碼

BidirectionalCollection

雙向集合與集合很是相似,只是它多了一個功能。與 Collection 繼承自 Sequence 相同,BidirectionalCollection 繼承自 Collection,可是雙向集合能夠向兩個方向任意移動。在集合當中,前進咱們已經有了 indexAfter 這個函數,因此爲了增長後退的功能,須要再增長一個名爲 indexBefore 的新函數,它將可讓咱們以相反的順序來遍歷集合。

1protocol Collection {2  //...3  func index(after index: Index) -> Index4}56protocol BidirectionalCollection: Collection {7  func index(before index: Index) -> Index8}9
複製代碼

你能夠試想一下若是是一個Collection普通集合,若是想要獲得最後一個元素該如何處理?

顯然你須要一個一個的遍歷,直到最後一個元素,這樣顯然太慢了,咱們更但願一步跳到最後,並當即將末尾的值返回,如今有了BidirectionalCollection,就很不同了,先檢查集合是否爲空,若是爲空,那麼直接返回nil便可;若是不是,咱們就須要取endIndex,而後經過indexBefore獲得endIndex的前一個索引,這樣就獲得了last元素。

1var last: Iterator.Element? {2    guard !self.isEmpty else { return nil }3    let indexOfLastItem = self.index(before: self.endIndex)4    return self[indexOfLastItem]5}
複製代碼

RandomAccessCollection

遵循此協議能夠更快地訪問值。你能夠直接跳轉到想要獲取的那個元素,而沒必要去逐步遍歷;RandomAccessCollection繼承BidirectionalCollection,能夠在常量時間訪問任何元素的集合,咱們經常使用的Array就是一個例子。

遵循RandomAccessCollection須要實現index(_:offsetBy:)和distance(from:to:)方法或者Index遵循Strideable協議。

MutableCollection

支持集合經過下標的方式改變自身的元素,即 array[index] = newValue。該協議在 Collection 的基礎上新增的 API 是下標subscript[5]必須提供的一個setter方法。

RangeReplaceableCollection

支持插入和刪除任意區間的元素集合;遵循RangeReplaceableCollection協議須要實現:

(1)空的初始化集合;

(2)實現replaceSubrange(_:with:),RangeReplaceableCollection協議提供了這個方法的默認實現,它用來替換當前集合中指定範圍中的元素。目標範圍和用來替換集合的長度能夠不一樣。

咱們再回頭看看文章開始處這張集合類型關係的圖譜,你會發現RangeReplaceableCollection和MutableCollection位於同一個層級,並無繼承關係,一些類型只符合MutableCollection(例如UnsafeMutableBufferPointer[6]),一些只適用於RangeReplaceableCollection(例如String.CharacterView),只有一部分同時遵照,如圖中所示的Array:

結語

Sequence 和 Collection 組成了 Swift 中集合類型的根基。而專門性的集合類型 BidirectionalCollection、RandomAccessCollection、MutableCollection 和 RandomAccessCollection 對你自定義的類型和算法的功能和性能特性提供了很是細粒度的控制,構成了強大的功能組合。

參考:/ Github /

相關文章
相關標籤/搜索