Swift?Sequence?Collection使用示例學(xué)習(xí)
一:Sequence
對于 Sequence 協(xié)議來說,表達的是既可以是一個有限的集合,也可以是一個無限的集合,而它只需要提供集合中的元素,和如何訪問這些元素的接口即可。

Sequence和Collection的關(guān)系.png
1.1 迭代器 Iterator
Sequence 是通過迭代器 Iterator 來訪問元素的,那么什么是迭代器?直接來看 for..in 函數(shù)
let numbers = [1, 2, 3, 4]
for num in numbers {
print(num)
}
for..in 函數(shù)其實是一種語法糖,他的本質(zhì)是怎么去調(diào)用的呢?編譯成 SIL 并定位到 main 函數(shù)中 for..in 的調(diào)用不重要的代碼我就直接省略了
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
...
// function_ref Collection<>.makeIterator()
%36 = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF : $@convention(method) <τ_0_0 where τ_0_0 : Collection, τ_0_0.Iterator == IndexingIterator<τ_0_0>> (@in τ_0_0) -> @out IndexingIterator<τ_0_0> // user: %37
%37 = apply %36<Array<Int>>(%31, %34) : $@convention(method) <τ_0_0 where τ_0_0 : Collection, τ_0_0.Iterator == IndexingIterator<τ_0_0>> (@in τ_0_0) -> @out IndexingIterator<τ_0_0>
%38 = tuple ()
dealloc_stack %34 : $*Array<Int> // id: %39
br bb1 // id: %40
bb1: // Preds: bb3 bb0
%41 = alloc_stack $Optional<Int> // users: %47, %44, %48
%42 = begin_access [modify] [static] %31 : $*IndexingIterator<Array<Int>> // users: %44, %46
// function_ref IndexingIterator.next()
%43 = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@inout IndexingIterator<τ_0_0>) -> @out Optional<τ_0_0.Element> // user: %44
%44 = apply %43<Array<Int>>(%41, %42) : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@inout IndexingIterator<τ_0_0>) -> @out Optional<τ_0_0.Element>
%45 = tuple ()
end_access %42 : $*IndexingIterator<Array<Int>> // id: %46
...
} // end sil function 'main'// function_ref Collection<>.makeIterator()
%36 = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF : $@convention(method) <τ_0_0 where τ_0_0 : Collection, τ_0_0.Iterator == IndexingIterator<τ_0_0>> (@in τ_0_0) -> @out IndexingIterator<τ_0_0> // user: %37
這里我們可以看到調(diào)用了一個 makeIterator() 的方法,這個方法需要兩個參數(shù)一個是在上文中創(chuàng)建 的 IndexingIterator , 一個是 Array 的引用。
// function_ref IndexingIterator.next()
%43 = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@inout IndexingIterator<τ_0_0>) -> @out Optional<τ_0_0.Element> // user: %44
- 這里調(diào)用
IndexingIterator的next()方法來遍歷數(shù)組中一個又一個的元素。
因此可以發(fā)現(xiàn)執(zhí)行for..in的時候,本質(zhì)上會通過Collection創(chuàng)建一個迭代器Iterator,然后把當(dāng)前的數(shù)組傳給迭代器Iterator,最后調(diào)用迭代器Iterator的next方法將數(shù)組的元素遍歷出來。
接著我們打開 Swift源碼 定位到 collection.swift 中的 IndexingIterator
@frozen
public struct IndexingIterator<Elements: Collection> {
@usableFromInline
internal let _elements: Elements
@usableFromInline
internal var _position: Elements.Index
/// Creates an iterator over the given collection.
@inlinable
@inline(__always)
public /// @testable
init(_elements: Elements) {
self._elements = _elements
self._position = _elements.startIndex
}
/// Creates an iterator over the given collection.
@inlinable
@inline(__always)
public /// @testable
init(_elements: Elements, _position: Elements.Index) {
self._elements = _elements
self._position = _position
}
}
extension IndexingIterator: IteratorProtocol, Sequence {
public typealias Element = Elements.Element
public typealias Iterator = IndexingIterator<Elements>
public typealias SubSequence = AnySequence<Element>
@inlinable
@inline(__always)
public mutating func next() -> Elements.Element? {
if _position == _elements.endIndex { return nil }
let element = _elements[_position]
_elements.formIndex(after: &_position)
return element
}
}可以看見 IndexingIterator 是一個接收泛型參數(shù) Collection 的結(jié)構(gòu)體,并且這個結(jié)構(gòu)體遵循了兩個協(xié)議,分別是 IteratorProtocol 和 Sequence 。 IteratorProtocol 是一個一次提供一個序列值的類型, 它和 Sequence 協(xié)議是息息相關(guān)的, 每次通過創(chuàng)建迭代器來訪問序列中的元素。
所以我們每次在使用 for..in 的時候,其實都是使用這個集合的迭代器來遍歷當(dāng)前集合或者序列當(dāng)中的元素。
1.2 IteratorProtocol協(xié)議
在 sequence.swift 文件中定位到 IteratorProtocol
public protocol IteratorProtocol {
/// The type of element traversed by the iterator.
associatedtype Element
mutating func next() -> Element?
}可以看到有一個關(guān)聯(lián)類型,實現(xiàn)該協(xié)議時需要執(zhí)行 Element 的類型,還有一個 next() 函數(shù)返回的是這個關(guān)聯(lián)類型。
1.3 Sequence協(xié)議
接著在 sequence.swift 文件中 定位到 Sequence
public protocol Sequence {
/// A type representing the sequence's elements.
associatedtype Element
/// A type that provides the sequence's iteration interface and
/// encapsulates its iteration state.
associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
/// A type that represents a subsequence of some of the sequence's elements.
// associatedtype SubSequence: Sequence = AnySequence<Element>
// where Element == SubSequence.Element,
// SubSequence.SubSequence == SubSequence
// typealias SubSequence = AnySequence<Element>
/// Returns an iterator over the elements of this sequence.
__consuming func makeIterator() -> Iterator
...
}- 可以看到這里也是有一個關(guān)聯(lián)類型
Element - 還有一個關(guān)聯(lián)類型
Iterator并且Iterator要遵循IteratorProtocol協(xié)議并且Iterator對于協(xié)議IteratorProtocol的關(guān)聯(lián)類型要與Sequence的關(guān)聯(lián)類型相等 - 有一個創(chuàng)建迭代器的方法
makeIterator()返回當(dāng)前關(guān)聯(lián)類型Iterator(這個就類似與車間一樣,Iterator就是一條流水線)
因此對于Sequence協(xié)議來說,表達的是既可以是一個有限的集合,也可以是一個無限的集合,而它只需要提供集合中的元素,和如何訪問這些元素的接口即可。
1.4 通過Sequence協(xié)議自定義有限的集合
直接上代碼
struct MyIterator: IteratorProtocol {
typealias Element = Int
let seq: MySequence
init(_ seq: MySequence) {
self.seq = seq
}
var count = 0
// 對于迭代器來說要遍歷當(dāng)前 `sequence` 中的元素
mutating func next() -> Int? {
guard count < self.seq.arrayCount else {
return nil
}
count += 1
return count
}
}
struct MySequence: Sequence {
typealias Element = Int
var arrayCount: Int
// 對于 sequence 來說需要一個迭代器
func makeIterator() -> MyIterator {
return MyIterator.init(self)
}
}
var seq = MySequence(arrayCount: 10)
for element in seq {
print(element)
}
打印結(jié)果:
1
2
3
4
5
6
7
8
9
10對于 sequence 來說需要一個迭代器,而對于對于迭代器來說要遍歷當(dāng)前 sequence 中的元素,所以需要 MyIterator 提供一個遍歷的構(gòu)造函數(shù) init(_ seq: MySequence)同樣的我們也可以創(chuàng)建一個無限的序列,但是在實際開發(fā)當(dāng)中一般不會出現(xiàn)這種場景,所以這里就不繼續(xù)了。
二:Collection
2.1 環(huán)形數(shù)組
環(huán)形數(shù)組是一種用于表示一個頭尾相連的緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu)。跟環(huán)形隊列差不多。接下來通過 Collection 來表達一個環(huán)形數(shù)組。
// 參照源碼
extension FixedWidthInteger {
/// Returns the next power of two.
@inlinable
func nextPowerOf2() -> Self {
guard self != 0 else {
return 1
}
return 1 << (Self.bitWidth - (self - 1).leadingZeroBitCount)
}
}
struct RingBuffer <Element>{
//ContiguousArray 可以理解為存粹的 Swift 的數(shù)組。和 OC 沒有任何關(guān)系的數(shù)組,它的效率比 Array 更快。
internal var _buffer: ContiguousArray<Element?>
internal var headIndex: Int = 0
internal var tailIndex: Int = 0
internal var mask: Int{
return self._buffer.count - 1
}
// 初始化方法
init(initalCapacity: Int) {
let capcatiy = initalCapacity.nextPowerOf2()
self._buffer = ContiguousArray<Element?>.init(repeating: nil, count:capcatiy)
}
// 移動環(huán)形數(shù)組的尾
mutating func advancedTailIndex(by: Int){
self.tailIndex = self.indexAdvanced(index: self.tailIndex, by: by)
}
// 移動環(huán)形數(shù)組的頭
mutating func advancedHeadIndex(by: Int){
self.headIndex = self.indexAdvanced(index: self.headIndex, by: by)
}
// 獲取元素下標(biāo)
func indexAdvanced(index: Int, by: Int) -> Int{
return (index + by) & self.mask
}
// 添加新元素
mutating func append(_ value: Element){
_buffer[self.tailIndex] = value
self.advancedTailIndex(by: 1)
if self.tailIndex == self.headIndex {
fatalError("out of bounds")
}
}
// 讀取元素
mutating func read() -> Element?{
let element = _buffer[self.headIndex]
self.advancedHeadIndex(by: 1)
return element
}
}這個結(jié)構(gòu)體通過一個 ContiguousArray 類型的 _buffer 來記錄環(huán)形數(shù)組的元素,ContiguousArray 可以理解為存粹的 Swift 的數(shù)組。和 OC 沒有任何關(guān)系的數(shù)組,它的效率比 Array 更快。
并且通過 headIndex 和 tailIndex 來表示環(huán)形數(shù)組的起始和結(jié)束的位置。以及一些方法。
在初始化方法中 nextPowerOf2 這個方法表示 2^n,這個表示使 capacity 始終為 2^n。
2.2 MutableCollection
MutableCollection 允許集合通過下標(biāo)修改自身元素。對于 MutableCollection 需要
- 定義
startIndex和endIndex屬性,表示集合起始和結(jié)束位置 - 定義一個只讀的下標(biāo)操作符
- 實現(xiàn)一個
index(after:)方法用于在集合中移動索引位置
extension RingBuffer: Collection, MutableCollection {
var startIndex: Int{
return self.headIndex
}
var endIndex: Int{
return self.tailIndex
}
subscript(position: Int) -> Element? {
get{
return self._buffer[position]
}
set{
self._buffer[position] = newValue
}
}
// 移動當(dāng)前索引的位置
func index(after i: Int) -> Int {
return (i + 1) & self.mask
}
}這里實現(xiàn)了 subscript 的 get 和 set ,對于 Collection 來說可以不提供 set ,這個時候我們就沒有辦法通過下標(biāo)的方式改變自身元素了,所以對 MutableColletion 來說下標(biāo)語法提供一個 setter 就行。就好比
var array = [1, 2, 3, 4] array[1] = 5
這里直接通過下標(biāo)修改元素
2.3 RangeReplaceableCollection
RangeReplaceableCollection 允許集合修改任意區(qū)間的元素。對于 RangeReplaceableCollection 我們需要提供一個默認(rèn)的 init 方法。其他的如果不需要 RangeReplaceableCollection 都有默認(rèn)的實現(xiàn)。
extension RingBuffer: RangeReplaceableCollection {
init() {
self.init(initalCapacity: 10)
}
mutating func remove(at i: Int) -> Element? {
var currentIndex = i
let element = _buffer[i]
self._buffer[currentIndex] = nil
switch i {
case self.headIndex:
self.advancedHeadIndex(by: 1)
default:
var nextIndex = self.indexAdvanced(index: i, by: 1)
while nextIndex != self.tailIndex {
self._buffer.swapAt(currentIndex, nextIndex)
currentIndex = nextIndex
nextIndex = self.indexAdvanced(index: currentIndex, by: 1)
}
}
return element
}
}對于環(huán)形數(shù)組來說這里的 remove 方法:首先都是把當(dāng)前位置的元素刪除,然后根據(jù)刪除的位置來進行判斷
- 當(dāng)刪除的位置剛好是
headIndex的位置,那么就將headIndex往后移動一個位置。 - 不是
headIndex的位置,就需要將后面的元素往前移一個位置,然后再把尾節(jié)點指向當(dāng)前空閑節(jié)點的位置。
2.4 BidirectionalCollection
BidirectionalCollection 可以向前或向后遍歷集合。 比如可以獲取最后一個元素、反轉(zhuǎn)序列、快速的獲取倒序等等。既然正序是通過 subscript 和 index(after:) 來實現(xiàn)的,那么倒序添加一個 index(before:) 就可以往前遞歸了,這就好像雙向鏈表 Remove 一樣,只不過雙向鏈表獲取的是值,而這里的集合獲取的都是索引。
extension RingBuffer: BidirectionalCollection{
func index(before i: Int) -> Int {
return (i - 1) & self.mask
}
}2.5 RandomAccessCollection
RandomAccessCollection 任意訪問集合元素。對于 RandomAccessCollection 需要實現(xiàn) index(_:offsetBy:) 和 distance(from:to:)
extension RingBuffer: RandomAccessCollection{
func index(_ i: Int, offsetBy distance: Int) -> Int {
return (i + distance) & self.mask
}
func distance(from start: Int, to end: Int) -> Int {
return end - start
}
}當(dāng)然,對于這個集合 (RingBuffer) 我們不去實現(xiàn)也是可以的。因為我們把 index(after:) 和 index(before:) 已經(jīng)實現(xiàn)了,對于系統(tǒng)來說,它是可以通過這兩個方法來推斷當(dāng)前要移動的距離。但是考慮到效率的問題,在這里直接實現(xiàn)會比系統(tǒng)去推斷要好得多,因為直接省去了系統(tǒng)推斷的時間。
使用示例代碼
var buffer: RingBuffer = RingBuffer<Int>.init(initalCapacity: 10)
for i in 0..<10 {
buffer.append(i)
}
print(buffer.index(0, offsetBy: 5))
打印結(jié)果:
5以上就是Swift Sequence Collection使用示例學(xué)習(xí)的詳細內(nèi)容,更多關(guān)于Swift Sequence Collection的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Swift Access Control訪問控制與斷言詳細介紹
這篇文章主要介紹了Swift Access Control訪問控制與斷言,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09
Objective-C和Swift的轉(zhuǎn)換速查手冊(推薦)
這篇文章主要給大家介紹了關(guān)于Objective-C和Swift的轉(zhuǎn)換速查手冊的相關(guān)資料,文中通過示例代碼介紹的非常詳細,非常推薦給大家參考學(xué)習(xí)使用,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)不2018-06-06

