
我們知道在 Swift 中 Sequence 是 集合類型結(jié)構(gòu) 中的基礎(chǔ)。而在 Sequence 協(xié)議的定義當(dāng)中我們可以看到有這么一句代碼 :
associatedtype Iterator : IteratorProtocol
我們看到有兩個(gè)關(guān)鍵字:associatedtype 與 IteratorProtocol
1.associatedtype
那么這個(gè) associatedtype 是什么意思呢?
看個(gè)例子就知道了:
第一步:
我們定義兩個(gè)協(xié)議 A,B,協(xié)議 B 中用到了協(xié)議 A
protocol A {}
protocol B {
func action(_ pA2: A)
}
第二步:
那么我們對(duì)協(xié)議 A 有兩個(gè)實(shí)現(xiàn): A1 與 A2.
我們對(duì)協(xié)議 B 有 B1 的實(shí)現(xiàn).代碼如下:
struct A1: A {}
struct A2: A {}
struct B1: B {
func action(_ pA2: A) {
print("我只認(rèn)對(duì)協(xié)議A實(shí)現(xiàn)的A2")
}
}
這個(gè)時(shí)候如果我們要使用 B1 的時(shí)候,可以這樣:
let action = B1()
action.action(A2())
action.action(A1())
///我只認(rèn)對(duì)協(xié)議A實(shí)現(xiàn)的A2
///我只認(rèn)對(duì)協(xié)議A實(shí)現(xiàn)的A2
第三步: 條件
這個(gè)時(shí)候我們?cè)?B1 中對(duì)傳入?yún)f(xié)議 A 的類型想要指定的類型實(shí)現(xiàn)而傳入非指定類型的實(shí)現(xiàn)編譯器就會(huì)報(bào)錯(cuò)怎么做?
有人講這樣:
struct B1: B {
func action(_ pA2: A2) {
print("我只認(rèn)對(duì)協(xié)議A實(shí)現(xiàn)的A2")
}
}
這樣子編譯器就會(huì)報(bào)錯(cuò):Type 'B1' does not conform to protocol 'B'。
這個(gè)時(shí)候我們的 associatedtype 就可以登場(chǎng)了:
protocol A {}
protocol B {
associatedtype F: A
func action(_ pA2: F)
}
struct A1: A {}
struct A2: A {}
struct B1: B {
func action(_ pA2: A2) {
print("我只認(rèn)對(duì)協(xié)議A實(shí)現(xiàn)的A2")
}
}
let action = B1()
action.action(A2())
///action.action(A1()) 這個(gè)實(shí)現(xiàn)就直接報(bào)錯(cuò)了:error: OptimizingCollections.playground:16:15: error: cannot convert value of type 'A1' to expected argument type 'A2' action.action(A1())
對(duì) associatedtype 總結(jié)就仁者見仁各有各的理解啦!小插曲結(jié)束進(jìn)入正題:↓
2.IteratorProtocol 與迭代器的邂逅
按住 Command 點(diǎn)進(jìn)去會(huì)發(fā)現(xiàn)該協(xié)議只有一個(gè)函數(shù):
associatedtype Element
public mutating func next() -> Self.Element?
以下用 next() 來(lái)表示.
可別看它就這么一句;小,但是 能量 很大?。?!
迭代器 是一個(gè)滿足 IteratorProtocol 協(xié)議的類型。
普及一個(gè)知識(shí):
序列 (Sequence)
具有相同類型的值的一個(gè)集合吧。比如簡(jiǎn)單的: [Int] ... 都是滿足 Sequence 協(xié)議
的。
那么有了序列就會(huì)有需要對(duì)值遍歷的訪問,那么就是靠創(chuàng)建一個(gè) 迭代器 來(lái)進(jìn)行對(duì)元素的訪問。
那么 next() 的作用就是每次調(diào)用的時(shí)候返回序列的下一個(gè)值,直到最后一個(gè)返回 nil
這里來(lái)開始剖析:
Self.Element
它是一個(gè)關(guān)聯(lián)類型用來(lái)指定 next() 所產(chǎn)生值的元素類型,比如我們經(jīng)常見到的 Iterator.Element 就是 IteratorProtocol 中的定義 Element。那么迭代器一般會(huì)用在那里呢?
答:自定義序列的擴(kuò)展類型,比如你想通過字符串 "YinYu" 轉(zhuǎn)換為:"["Y", "i", "n", "Y", "u"]" 的擴(kuò)展類等等等等多的用途!
迭代器在結(jié)構(gòu)上是單向的,顧名思義只能一路 next
對(duì)于迭代器類似的操作我們一般都是用
for <#item#> in <#items#> {
<#code#>
}
來(lái)完成。 這個(gè)一般情況是不推薦使用的。我們要玩就要玩高級(jí)的。不然怎么成長(zhǎng)呢?是吧!
接下來(lái)我們來(lái)完成這樣一個(gè)例子的實(shí)現(xiàn),來(lái)為您以后 “拋磚引玉” 一下
首先完成一個(gè) PrefixIterator 實(shí)現(xiàn) IteratorProtocol協(xié)議的 struct。
public struct PrefixIterator: IteratorProtocol {
let string: String
var offset: String.Index
init(string: String) {
self.string = string
offset = string.startIndex
}
mutating public func next() -> String? {
guard offset < string.endIndex else { return nil }
let previousSet = offset
offset = string.index(after: offset)
return String(string[previousSet..<offset])
}
}
我們分析上面主要有: mutating,字符串切片(String(string[previousSet..<offset]))
那么有了迭代器,就需要一個(gè)裝載迭代器的且滿足 Sequence 協(xié)議的 struct.
因?yàn)樵?Sequence 中我們發(fā)現(xiàn)有這樣的一個(gè)函數(shù):
associatedtype Iterator : IteratorProtocol
///Returns an iterator over the elements of this sequence
public func makeIterator() -> Self.Iterator
那么這里我們的 PrefixIterator 就相當(dāng)于 Self.Iterator 類型.
接下來(lái)就實(shí)現(xiàn)對(duì) Sequence協(xié)議的構(gòu)造。
public struct PrefixSequence: Sequence {
let string: String
public func makeIterator() -> PrefixIterator {
return PrefixIterator(string: string)
}
}
既然實(shí)現(xiàn)了 Sequence協(xié)議,那么我們就可以得到 Map, filter, forEach, dropFirst... 這些集合類型常用的函數(shù)方法。
利用 Map依次返回一個(gè)字母。從而達(dá)到 "YinYu" 到 "Y", "i", "n", "Y", "u" 的轉(zhuǎn)換
這樣一個(gè) String 擴(kuò)展方法就出來(lái)了:
extension String {
func stringToArr() -> [String] {
return PrefixSequence(string: self).map { $0 }
}
}
let strs = "YinYu".stringToArr()
/// ["Y", "i", "n", "Y", "u"]
當(dāng)然如果你想輸出的字面都是大寫很簡(jiǎn)單:
$0.uppercased() 這樣就OK啦.
值語(yǔ)義
我們都知道 結(jié)構(gòu)體(Struct) 枚舉(enum) 是 值類型。
當(dāng)創(chuàng)建一個(gè) class(類) 的時(shí)候,class 是 引用類型。
對(duì)于引用類型的操作我們是要萬(wàn)分小心的,引用類型是具有統(tǒng)一性的一般用 === 判斷兩個(gè)變量是否引用了同一個(gè) object。即:指針相等。
那 == 呢? 當(dāng)然是: 結(jié)構(gòu)相等
一般我們通過值類型是否執(zhí)行 深復(fù)制來(lái)判斷是否具有 值語(yǔ)義.
有深復(fù)制就會(huì)有 潛復(fù)制:
當(dāng) Struct 中包含引用類型,這個(gè)時(shí)候 Struct 進(jìn)行賦值給其它的變量的時(shí)候所發(fā)生的復(fù)制行為會(huì)存在引用類型的內(nèi)容是不會(huì)復(fù)制,其引用本身會(huì)被復(fù)制。這種行為就是 淺復(fù)制.
AnyIterator
可以對(duì)其它的迭代器進(jìn)行一個(gè)包裝,從而迷惑使用者。
這里要說(shuō)的就是 AnyIterator 在這樣的情況下 var 新迭代器 = AnyIterator(舊迭代器) 的時(shí)候, 新迭代器是 不具有 值語(yǔ)義的。這種情況下舊迭代器與新迭代器就不是單獨(dú)的了,新迭代器就不是一個(gè)結(jié)構(gòu)體。 新迭代器是以一個(gè)類實(shí)例。
下面代碼見分曉:
var ierator1 = stride(from: 0, to: 10, by: 1).makeIterator()
ierator1.next() /// 0
ierator1.next() /// 1
var ierator2 = ierator1
ierator1.next() /// 2
ierator1.next() /// 3
ierator2.next() /// 2
ierator2.next() /// 3
var ierator3 = AnyIterator(ierator1)
var ierator4 = ierator3
ierator3.next() /// 4
ierator4.next() /// 5
ierator3.next() /// 6
ierator3.next() /// 7
AnySequence
似乎與 Iterator 與 Sequence 一樣,也存在著 AnySequence.
AnyIterator 也是對(duì) IteratorProtocol 協(xié)議的實(shí)現(xiàn).那么配合 nest() 與對(duì)應(yīng)的 AnySequence,可以得到在不定義任何新類型的情況下創(chuàng)建迭代器序列。