迭代器模式(Iterator Pattern)屬于行為型模式。Iterator pattern 提供了循環(huán)集合的標(biāo)準(zhǔn)方法。
Iterator pattern 包含以下兩部分:

- IteratorProtocol:Swift 中的
IterableProtocol協(xié)議定義了一個可以使用 for in 循環(huán)迭代的類型。 - Iterator Object:想要進(jìn)行迭代的對象。一般,Iterator object 不直接遵守
IteratorProtocol協(xié)議,而是遵守Sequence協(xié)議。Sequence協(xié)議遵守IteratorProtocol協(xié)議。通過遵守Sequence協(xié)議可以直接獲得許多高級函數(shù),例如,map、filter等。
如果你對該協(xié)議不了解,可以查看IterableProtocol、Sequence文檔。
在這篇文章中,將創(chuàng)建自定義 struct。該對象遵守Sequence協(xié)議,并使用Sequence協(xié)議中方法進(jìn)行排序。
何時使用 Iterator Pattern
當(dāng)擁有一組有序?qū)ο蟮念惢蚪Y(jié)構(gòu),并且希望使用 for in 循環(huán)使其可迭代時,請使用迭代器模式。
示例
這篇文章將會創(chuàng)建 Queue 對象。Queue 是一個列表,只能從尾部插入對象,從頭部移除對象。這樣可以確保第一個入隊的對象也是第一個出隊的對象,先進(jìn)先出(first come, first serve)。
創(chuàng)建Queue.swift文件,并添加以下代碼:
import Foundation
public struct Queue<T> {
// 數(shù)組元素可以為任意類型。
private var array: [T?] = []
// Queue 的頭部是數(shù)組第一個元素
private var head = 0
// 確認(rèn)數(shù)組是否為空
public var isEmpty: Bool {
return count == 0
}
// Queue 內(nèi)對象數(shù)量
public var count: Int {
return array.count - head
}
// 添加對象到 Queue
public mutating func enqueue(_ element: T) {
array.append(element)
}
// 從 Queue 隊列移除對象
public mutating func dequeue() -> T? {
guard head < array.count,
let element = array[head] else {
return nil
}
array[head] = nil
head += 1
let percentage = Double(head)/Double(array.count)
if array.count > 50,
percentage > 0.25 {
array.removeFirst(head)
head = 0
}
return element
}
}
在上述代碼中,enqueue()方法用于添加對象到 Queue;dequeue()方法用于從 Queue 隊列移除對象。
創(chuàng)建Ticket.swift文件,并添加以下代碼:
import Foundation
public struct Ticket {
var description: String
var priority: PriorityType
enum PriorityType {
case low
case medium
case high
}
init(description: String, priority: PriorityType) {
self.description = description
self.priority = priority
}
}
進(jìn)入ViewController.swift文件,在viewDidLoad()方法內(nèi)添加以下代碼:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 初始化 Queue 對象,并添加四個對象。
var queue = Queue<Ticket>()
queue.enqueue(Ticket(description: "Wireframe Tinder for dogs app", priority: .low))
queue.enqueue(Ticket(description: "Set up 4k monitor for Josh", priority: .medium))
queue.enqueue(Ticket(description: "There is smoke coming out of my laptop", priority: .high))
queue.enqueue(Ticket(description: "Put googly eyes on the Roomba", priority: .low))
// 可以看到 dequeue 的是第一個添加的對象
let element = queue.dequeue()
print((element?.description ?? "No Description") + "\n")
}
在實(shí)際應(yīng)用中,肯定希望能夠按照優(yōu)先級對 Ticket 進(jìn)行排序。根據(jù)現(xiàn)在的情況,只能編寫多個 if 語句進(jìn)行排序。使用 Swift 內(nèi)置的排序函數(shù)能夠節(jié)省大量時間。
如果現(xiàn)在嘗試使用 for in 、sorted()進(jìn)行排序,編譯器會報錯。因為使用這些方法前需要先遵守Sequence協(xié)議。為Queue添加以下 extension:
extension Queue: Sequence {
public func makeIterator() -> IndexingIterator<ArraySlice<T?>> {
// 只枚舉非空對象
let nonEmptyValues = array[head ..< array.count]
return nonEmptyValues.makeIterator()
}
}
遵守Sequence協(xié)議時,有兩點(diǎn)需要注意:
- 第一點(diǎn)為關(guān)聯(lián)類型(associated type),也就是迭代器(Iterator)。在上述代碼中,associated type 為
IndexingIterator。如果集合沒有聲明自定義迭代器,則默認(rèn)使用IndexingIterator。 - 第二點(diǎn)為
Iterator協(xié)議,其為makeIterator函數(shù)不可缺少的。Iterator協(xié)議為 class 、struct 構(gòu)造迭代器。
在ViewController.swift文件中viewDidLoad()方法內(nèi)添加以下代碼:
override func viewDidLoad() {
...
// 枚舉 queue 并輸出
print("List of Tickets in queue:")
for ticket in queue {
print(ticket?.description ?? "No Description")
}
}
輸出如下:
List of Tickets in queue:
Set up 4k monitor for Josh
There is smoke coming out of my laptop
Put googly eyes on the Roomba
在使用排序函數(shù)前,為Ticket添加以下 extension:
extension Ticket {
var sortIndex: Int {
switch self.priority {
case .low:
return 0
case .medium:
return 1
case .high:
return 2
}
}
}
為優(yōu)先級設(shè)置數(shù)字能夠簡化排序,排序時將使用sortIndex做為依據(jù)。在ViewController.swift文件中viewDidLoad()方法內(nèi)添加以下代碼:
override func viewDidLoad() {
...
// 進(jìn)行排序
let sortedTickets = queue.sorted {
$0!.sortIndex > ($1?.sortIndex)!
}
// 循環(huán) sortedTickets 數(shù)組,將元素添加到 sortedQueue
var sortedQueue = Queue<Ticket>()
for ticket in sortedTickets {
sortedQueue.enqueue(ticket!)
}
// 輸出 sotedQueue 內(nèi)對象,可以看到 first in, firt out。
print("\n")
print("Tickets sorted by priority:")
for ticket in sortedQueue {
print(ticket?.description ?? "No Description")
}
如此輕松地對集合進(jìn)行排序是一項強(qiáng)大的功能。隨著集合變大,排序功能將變得更有價值。
總結(jié)
通過遵守IteratorProtocol協(xié)議,可以自定義迭代對象的方式。例如,可以只實(shí)現(xiàn)一個next()方法,在該方法內(nèi)返回迭代中的下一個對象。通常,極少需要直接遵守IteratorProtocol協(xié)議。即使需要自定義迭代器,遵守Sequence協(xié)議也是一個更好的選擇。
以下是 Iterator Pattern 的關(guān)鍵點(diǎn):
- Iterator pattern 提供了使用 for in 語法循環(huán)遍歷集合的標(biāo)準(zhǔn)方法。
- 自定義對象最好遵守
Sequence協(xié)議,而非直接遵守IteratorProtocol協(xié)議。 - 通過遵守
Sequence協(xié)議,能夠直接獲得更為高級的功能(如map、filter等)。
Demo名稱:IteratorPattern
源碼地址:https://github.com/pro648/BasicDemos-iOS/tree/master/IteratorPattern
參考資料: