Swift 核心协议揭秘:从 Sequence 到 Collection,你离标准库设计者只差这一步

swift是面向协议编程,果然名不虚传

swift中的Iterator初步认识

IteratorProtocol 协议

swift 复制代码
public protocol IteratorProtocol<Element> {
    associatedtype Element
    mutating func next() -> Element?
}

这样所有遵守了IteratorProtocol协议的类型,都是可以使用next方法的,这已经很完美了。但是!迭代器只能消费一次, 这里举一个不恰当的例子:

swift 复制代码
let numbers = [10, 20, 30]
// 从序列要一个迭代器(IteratorProtocol)
var it = numbers.makeIterator()
// 一步一步消费
print(it.next() as Any)  // Optional(10)
print(it.next() as Any)  // Optional(20)
print(it.next() as Any)  // Optional(30)
print(it.next() as Any)  // nil ------ 已经到头了
// 同一个 it 再 next,永远是 nil(状态已经走到结束)
print(it.next() as Any)  // nil
print(it.next() as Any)  // nil

想再从头遍历一遍,不能指望复活这个it,只能再向序列要一个新的迭代器:

python 复制代码
var it2 = numbers.makeIterator()
print(it2.next() as Any)  // Optional(10) ------ 又从第一个开始

但是这里的makeIterator是sequence协议要求提供的东西,之所以说这个例子不恰当,是因为我似乎在用已经解决的问题去回答问题,这里不应该把sequence牵涉进来。

那么,接下来的例子将非常合适。

swift 复制代码
struct CountFromTo: IteratorProtocol {
    var current: Int
    let end: Int
    init(from: Int, through: Int) {
        current = from; end = through
    }
    mutating func next() -> Int? {
        guard current <= end else { return nil }
        defer { current += 1 }
        return current
    }
}
var it = CountFromTo(from: 3, through: 5)
while let x = it.next() { print(x) }   // 耗尽
print(it.next()) //nil,因为之前已经耗尽了
// 不能复活it,只能再来一个新的迭代器实例
var it2 = CountFromTo(from: 3, through: 5)
print(it2.next() as Any)   // 又从 3 开始
var it = CountFromTo(from: 3, through: 5)

现在假设我们是swift标准库团队开发人员,要实现Array,我们需要提供给开发者类似以下这些功能

  • for x in arr
  • arr.map { }、arr.filter { }的功能
  • 和别的"能挨个读一遍某个东西"的方法用同一套API

下标 arr[i]可以实现"挨个读一遍"的功能,但是正如我们提到的for x in arr / arr.map这种功能,它们只想对每个元素做某事,不需要关心下标。

scss 复制代码
for x in arr {
    print(x)
}
//可以通过这种方式实现
var __iterator = arr.获取iterator()
while let x = __iterator.next() {
    print(x)
}

map大致如下

swift 复制代码
func mapSimple<T>(_ transform: (Element) -> T) -> [T] {
        var result: [T] = []
        var it = 获取iterator()
        while let x = it.next() {
            result.append(transform(x))
        }
        return result
    }

可以看出不论实现哪个功能都需要array有一个获取iterator的方法,给这个方法起名叫做makeIterator,也就是说array既要有next方法,又要有makeiterator的方法,我们把这两个方法都放入一个起名为sequence的protocol中,这就是sequence的由来了。Sequence 是 Swift 中最轻量 的遍历协议。一个类型只要遵守 Sequence,就能用 for-in 遍历。实现了 Sequence的结构体或类 必须关联一个 遵守 IteratorProtocol 的类型,

  • Sequence工厂:生产迭代器

  • IteratorProtocol产品:实际遍历逻辑

所以不能说实现了Sequence就是是实现了IteratorProtocol.

仅仅实现Sequence协议,你的类型就能享受所有Sequence的默认extension方法:mapfilterreducecontains(Element: Equatable)reversed。

rust 复制代码
//Sequence 协议:
  protocol Sequence<Element> {
      associatedtype Element where Self.Element == Self.Iterator.Element
      associatedtype Iterator: IteratorProtocol
      func makeIterator() -> Iterator
  }

Sequence 够用了吗?

Sequence 只保证:能 makeIterator(),按顺序 next() 一个个拿。

适合:for-inmapfilter 等扫一遍的事。

但日常还会遇到:

  • 第 3 个元素是谁?(随机访问某一位)

  • 有多少个?(count)

  • 第一个、最后一个下标怎么表示?

只靠 Iterator:只能往后走,不能跳到中间,也不一定有常数时间的长度概念(有些序列是无限的、或算长度很贵)。

所以要在 Sequence 上再叠一层:能按下标(或索引)访问、有明确首尾------这就是 Collection 的由来。

Collection 在解决什么?

在能遍历之上,再约定像容器一样用下标访问的能力。典型能力包括(概念上):

  • startIndex / endIndex

  • 能用 collection[index] 读元素(subscript

  • 索引可以 index(after:) 往后走(不一定只是 Int + 1,字符串的 Index 就复杂)

  • 往往还能提供 count(有的集合是 O(n) 算出来)

swift 复制代码
public protocol Collection: Sequence {
    associatedtype Index: Comparable
    var startIndex: Index { get }
    var endIndex: Index { get }
    subscript(position: Index) -> Element { get }
    func index(after i: Index) -> Index
}

`Collection` 协议**继承自** `Sequence` 协议,因此任何遵守 `Collection` 的类型**自动满足** `Sequence` 的所有要求。

Array 是最典型的 Collection:下标是 Int,从 0count-1

python 复制代码
Sequence  ←── 更基础:只保证能遍历
   ↑
Collection ←── 继承 Sequence,并加:索引 + 下标访问 + ...

在 Swift 里遵守 Collection 只能说明它是可按索引访问的一段序列,不一定是自己拥有一块独立存储的容器。例如Range,遵守RandomAccessCollection属于 Collection 一族

scss 复制代码
let r = 0..<10
print(r.count)           // 10
print(r[r.startIndex]) // 0

这里并没有一个数组在内存里存 0,1,2,...,9Range 只是用起点、终点描述区间,按需算出元素。它更像区间视图,不是传统意义上的数组那种容器。

本文使用 文章同步助手 同步

相关推荐
开心就好20252 小时前
使用Edge和ADB进行Android Webview远程调试的完整教程
前端·ios
开心就好20254 小时前
iOS应用上架全流程:从证书申请到发布避坑指南
后端·ios
梦想不只是梦与想5 小时前
flutter 与 Android iOS 通信?以及实现原理(一)
android·flutter·ios·methodchannel·eventchannel·basicmessage
冰凌时空7 小时前
30 Apps 第 1 天:待办清单 App —— 数据层完整设计
前端·ios
2501_915909068 小时前
Xcode从入门到精通:全面解析iOS开发IDE的核心功能与实际应用指南
ide·vscode·ios·个人开发·xcode·swift·敏捷流程
懋学的前端攻城狮8 小时前
登录与注册:不止于UI,更关乎安全与用户体验的闭环
ios
卢锡荣9 小时前
单芯双 C 盲插,一线通显电 ——LDR6020P 盲插 Type‑C 显示器方案深度解析
c语言·开发语言·ios·计算机外设·电脑
华盛AI9 小时前
Lovable开发平台,生成安卓和iOS都能运行的原生App方案(用Kotlin或者Switf编写)
android·ios·kotlin
库奇噜啦呼10 小时前
【iOS】alloc & init & new 源码学习
学习·ios·cocoa