一、核心结论
Combine 是一个受控的数据流系统(pull-controlled push system)
- 数据流方向:Publisher → Subscriber
- 控制流方向:Subscriber → Publisher(通过 Subscription.request)
想象一个报社 → 邮递员 → 订阅者的模型:
- 报社(Publisher) — 生产报纸
- 你(Subscriber) — 消费报纸
- 订阅合同(Subscription) — 控制"送几份、什么时候送、能不能取消"
数据流(报纸的流向):
报社 → 邮递员 → 你家门口
控制流(谁说了算):
你 → 合同 → 报社
关键点来了 —— 不是报社想送多少就送多少,而是你决定要几份
这就是所谓的 "受控的数据流" ,英文叫 pull-controlled push system,意思是:
- 数据是被推送过来的(push)
- 但推多少由消费者拉取控制(pull-controlled)
三大核心角色:
| 角色 | 职责 |
|---|---|
| Publisher | 生产数据 |
| Subscriber | 消费数据 |
| Subscription | 控制数据流(背压 + 生命周期) |
Subscription 是怎么产生的?
还是用报社比喻:
你去报社签合同的过程就是 subscribe,签完合同那一刻,合同(Subscription)就产生了。
你(Subscriber)去找报社(Publisher)说"我要订阅"
↓
报社创建一份合同(Subscription)
↓
报社把合同交给你:publisher.receive(subscription:)
↓
你拿到合同,告诉报社"先给我发3份":subscription.request(.max(3))
↓
报社开始送报纸
import Combine
// ============================================
// 第一步:自定义一个 Subscriber
// ============================================
class MySubscriber: Subscriber {
// 告诉 Publisher:我接收的数据类型是 Int,错误类型是 Never
typealias Input = Int
typealias Failure = Never
// 保存合同,不然合同会被释放,数据就停了
var subscription: Subscription?
// ✅ 第四步:publisher 把合同交给我
func receive(subscription: Subscription) {
print("📋 收到合同了")
self.subscription = subscription
// ✅ 第五步:我告诉 publisher,先给我发 2 个
subscription.request(.max(2))
print("🙋 我要了 2 个数据")
}
// ✅ 第六步:数据流过来了
func receive(_ input: Int) -> Subscribers.Demand {
print("📦 收到数据:\(input)")
// 返回值表示:收到这个之后,再追加要几个
// .none 表示不追加,维持之前的配额
return .none
}
// ✅ 最后:流结束了
func receive(completion: Subscribers.Completion<Never>) {
print("✅ 数据流结束:\(completion)")
}
}
// ============================================
// 第二步:创建 Publisher
// ============================================
let publisher = [1, 2, 3, 4, 5].publisher
// ============================================
// 第三步:创建 Subscriber
// ============================================
let subscriber = MySubscriber()
// ============================================
// 触发整个流程!
// ============================================
publisher.subscribe(subscriber) // ← 就这一行启动了所有事情
输出结果
📋 收到合同了
🙋 我要了 2 个数据
📦 收到数据:1
📦 收到数据:2
✅ 数据流结束:finished
注意:虽然 publisher 有 5 个数据,但我们只要了 2 个,所以只收到 1 和 2。但因为这是数组 publisher,它发完你要的之后就结束了,所以还是触发了 completion。
对应流程图
publisher.subscribe(subscriber)
↓
publisher 内部 new Subscription() ← Combine 帮你做的
↓
subscriber.receive(subscription:) ← 你的代码:收到合同
↓
subscription.request(.max(2)) ← 你的代码:要2个
↓
subscriber.receive(1) ← 你的代码:收到数据
subscriber.receive(2) ← 你的代码:收到数据
↓
subscriber.receive(completion: .finished) ← 你的代码:结束
二、核心协议定义
Publisher
protocol Publisher {
associatedtype Output
associatedtype Failure: Error
func receive<S: Subscriber>(subscriber: S)
}
要点:
- 不直接发数据
- 必须先建立订阅
Subscriber
protocol Subscriber {
associatedtype Input
associatedtype Failure: Error
func receive(subscription: Subscription)
func receive(_ input: Input) -> Subscribers.Demand
func receive(completion: Subscribers.Completion<Failure>)
}
要点:
- 控制数据流(demand)
- 接收 value 和 completion
Subscription
protocol Subscription {
func request(_ demand: Subscribers.Demand)
func cancel()
}
要点:
- Publisher 创建
- Subscriber 使用
- 控制流量 + 生命周期
三、完整执行流程(从 sink 开始)
代码:
publisher
.print()
.sink(receiveCompletion: { _ in }, receiveValue: { _ in })
执行顺序:
1. sink 被调用
- 创建一个 Subscriber(Sink)
2. 建立订阅
publisher.receive(subscriber: subscriber)
3. Publisher 响应订阅
- 创建 Subscription
- 调用:
subscriber.receive(subscription: subscription)
4. Subscriber 请求数据
subscription.request(.unlimited)
说明:
- sink 默认策略:无限需求
5. 数据流动条件成立
必须满足:
- 已建立 subscription
- 已 request demand
否则不会发数据
6. Publisher 发送数据
publisher.send(1)
7. 数据传递路径
Publisher → Subscription → Subscriber.receive(value)
8. 完成事件
Publisher → Subscriber.receive(completion)
四、subscribe 的本质
subscribe(receive(subscriber:))做三件事:
- 建立 Publisher 和 Subscriber 连接
- 创建 Subscription
- 把 Subscription 交给 Subscriber
结论:
- subscribe 只负责“连接”
- 不负责“发送数据”
五、Subscription 的本质
Subscription 是数据流控制器
职责:
- 记录 demand
- 控制是否允许发送
- 处理 cancel
关键点:
- 不主动发送数据
- 只是控制通道
六、Demand(背压机制)
定义:
enum Subscribers.Demand {
case unlimited
case max(Int)
}
含义:
- Subscriber 告诉 Publisher:我能处理多少
规则:
- Publisher 不能超过 demand 发送数据
七、数据流 vs 控制流
数据流:
Publisher → Subscriber
控制流:
Subscriber → Subscription → Publisher
总结:
- Subscriber 决定“要多少”
- Publisher 决定“怎么发”
- Subscription 执行控制
八、不同 Publisher 行为差异
自动发送:
- Empty:立即 finished
- Just:一个值 + finished
- CurrentValueSubject:先发当前值
手动发送:
- PassthroughSubject:必须 send()
九、PassthroughSubject 为什么 sink 没输出
let publisher = PassthroughSubject<Int, Never>()
publisher.sink { ... }
原因:
- 已订阅 ✔
- 已 request ✔
- 没有事件 ❗
只有:
publisher.send(1)
才会触发输出
十、.print() 的作用
.print() 只是调试工具,会打印:
- receive subscription
- request
- value
- completion
注意:
- 不影响逻辑
- 只是观察行为
十一、常见误区
误区 1:
- sink 只是触发订阅 ❌
- sink = 创建 Subscriber + 订阅 + request ✔
误区 2:
- Subscription 是 Subscriber 创建 ❌
- Subscription 是 Publisher 创建 ✔
误区 3:
- Subscription 发送数据 ❌
- Publisher 决定发送 ✔
误区 4:
- subscribe 会触发数据 ❌
- request 才决定是否能发 ✔
十二、完整模型
1. sink 创建 Subscriber
2. publisher.receive(subscriber)
3. Publisher 创建 Subscription
4. subscriber.receive(subscription)
5. subscriber.request(demand)
6. Publisher 根据 demand 决定发送
7. Subscription 控制转发
8. subscriber.receive(value)
十三、一句话理解
Combine = 一个由 Subscription 控制、由 Subscriber 驱动、由 Publisher 提供数据的流系统