更多内容,欢迎关注公众号:Swift花园
喜欢文章?不如来点赞关注吧
这一篇文章将聚焦在迭代器设计模式。Swift标准库中大量地使用了这种模式,它们是一些你需要创建迭代器时会用到的协议。不过老实说:我自己从来没直接实现过这种模式。😅
事实上,99%的情况你都不需要面对这种模式,因为Swift内建的迭代器已经惊人的好用了。尽量用序列,数组和字典,而不是直接实现这个模式。不过,有机会了解事物的底层工作机制,总归是好的,不是吗? 🙃
什么是迭代器设计模式?
如字面上说的,这种模式让你能够迭代一个合集里的元素。设计模式四人帮的书上有一个定义:
提供一种方法,访问一个容器对象中的各个元素,而又不暴露该对象的内部细节。
简单来说,迭代器为你提供一个接口,让你可以遍历合集,不用关心这个合集是怎么实现的。下面是一个用字符串迭代器演示上面定义的粒子。
import Foundation
protocol StringIterator {
func next() -> String?
}
class ArrayStringIterator: StringIterator {
private let values: [String]
private var index: Int?
init(_ values: [String]) {
self.values = values
}
private func nextIndex(for index: Int?) -> Int? {
if let index = index, index < self.values.count - 1 {
return index + 1
}
if index == nil, !self.values.isEmpty {
return 0
}
return nil
}
func next() -> String? {
if let index = self.nextIndex(for: self.index) {
self.index = index
return self.values[index]
}
return nil
}
}
protocol Iterable {
func makeIterator() -> StringIterator
}
class DataArray: Iterable {
private var dataSource: [String]
init() {
self.dataSource = ["🐶", "🐔", "🐵", "🦁", "🐯", "🐭", "🐱", "🐮", "🐷"]
}
func makeIterator() -> StringIterator {
return ArrayStringIterator(self.dataSource)
}
}
let data = DataArray()
let iterator = data.makeIterator()
while let next = iterator.next() {
print(next)
}如你所见,有两个主要的协议,各自都有一个简单的实现。 DataArray类现在就像一个真正的数组,内部的元素可以用循环来遍历。让我们再深挖一下理论,用 Swift 标准库里的组件来重新实现上面的例子。😉
Swift中的自定义序列
Swift有一个内建的序列协议,可以帮助你创建迭代器。实现你自己的序列其实就是做一件事:通过创建自定义的迭代器隐藏内部的数据结构体。你只需要存储当前的索引,然后在每一次 next 函数被调用时返回下一个元素。😛
import Foundation
struct Emojis: Sequence {
let animals: [String]
func makeIterator() -> EmojiIterator {
return EmojiIterator(self.animals)
}
}
struct EmojiIterator: IteratorProtocol {
private let values: [String]
private var index: Int?
init(_ values: [String]) {
self.values = values
}
private func nextIndex(for index: Int?) -> Int? {
if let index = index, index < self.values.count - 1 {
return index + 1
}
if index == nil, !self.values.isEmpty {
return 0
}
return nil
}
mutating func next() -> String? {
if let index = self.nextIndex(for: self.index) {
self.index = index
return self.values[index]
}
return nil
}
}
let emojis = Emojis(animals: ["🐶", "🐔", "🐵", "🦁", "🐯", "🐭", "🐱", "🐮", "🐷"])
for emoji in emojis {
print(emoji)
}因此,Sequence 协议相当于前一个例子中的那个 Iterable 协议,而 IteratorProtocol 则类似于对应的 StringIterator,不过更像 Swift 风格,并且更通用。
你现在已经了解如何创建自定义序列了,这在隐藏数据结构体和提供泛型可迭代接口时会很有用。 🤔
我还想介绍一个非常强大的东西,它是在 Swift 标准库里头。准备好了吗?
Swift 中的自定义 Collection
Collection 是比序列更进一步的东西,里面的元素可以通过下标访问。Collection 还定义了 startIndex 和 endIndex,并且每个独立元素都可以多次访问。听起来不错吧?👍
某些情况下,创建自定义 Collection 类型非常有用。举个例子,如果你想消除可选型。想象一个分类收藏的机制,对于每个分类都有一组收藏。你需要处理空的情况和不存在的情况。利用自定义 Collection,你可以在你的自定义数据结构里隐藏额外的代码,只暴露一个干净的接口。😍
class Favorites {
typealias FavoriteType = [String: [String]]
private(set) var list: FavoriteType
public static let shared = Favorites()
private init() {
self.list = FavoriteType()
}
}
extension Favorites: Collection {
typealias Index = FavoriteType.Index
typealias Element = FavoriteType.Element
var startIndex: Index {
return self.list.startIndex
}
var endIndex: Index {
return self.list.endIndex
}
subscript(index: Index) -> Iterator.Element {
return self.list[index]
}
func index(after i: Index) -> Index {
return self.list.index(after: i)
}
}
extension Favorites {
subscript(index: String) -> [String] {
return self.list[index] ?? []
}
func add(_ value: String, category: String) {
if var values = self.list[category] {
guard !values.contains(value) else {
return
}
values.append(value)
self.list[category] = values
}
else {
self.list[category] = [value]
}
}
func remove(_ value: String, category: String) {
guard var values = self.list[category] else {
return
}
values = values.filter { $0 == value }
if values.isEmpty {
self.list.removeValue(forKey: category)
}
else {
self.list[category] = values
}
}
}
Favorites.shared.add("apple", category: "fruits")
Favorites.shared.add("pear", category: "fruits")
Favorites.shared.add("apple", category: "fruits")
Favorites.shared["fruits"]
Favorites.shared.remove("apple", category: "fruits")
Favorites.shared.remove("pear", category: "fruits")
Favorites.shared.list我知道这个例子看起来有点呆,不过很恰当地演示了为什么合集比序列更强大。🤐
我的公众号
这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~
