Combine 是 Apple 推出的一个响应式编程框架,专门用于处理异步事件和数据流。它可以帮助开发者以声明性的方式处理复杂的异步任务,例如网络请求、定时器、UI 更新等。
在 SwiftUI 中,Combine 被大量用来处理数据绑定、网络请求、UI 状态管理等场景。
为什么需要Combine?
在没有Combine之前,传统异步任务通常会遇到以下问题:
1、多个异步操作需要串联执行。
2、每个异步回调都需要单独处理错误。
3、需要手动切换主线程和后台线程。
例如,需要下载一个图片:
downloadImage { image in
saveToLocal(image) { success in
uploadToServer(image) { result in
// 嵌套太深,难以阅读和维护
}
}
}
这个代码就是回调地狱(Callback Hell),代码嵌套太深,难以阅读和维护。
Combine通过“数据流”的思想,将异步事件统一为“发布者-订阅者”模式:
downloadImage()
.saveToLocal()
.uploadToServer()
.receive(on: DispatchQueue.main) // 自动切换到主线程
.sink { result in
print("全部完成")
}
Combine的代码可读性更高,通过链式调用替代嵌套回调,在数据流末端统一处理错误。
利用操作符可以更好的处理数据流的复杂逻辑,并控制数据在主线程或后台线程中处理。
核心概念
Combine类似新闻报道,主要分为三个阶段:事件发生后,媒体采访并加工新闻,用户查看新闻。
这三个阶段对应的是:
事件(Publisher),负责生产新闻,属于新闻来源。
媒体(Operator),负责采访和加工新闻,用于处理、加工报道。
用户(Subscriber),负责接收查看的问题。
1、Publisher(发布者)
发布者负责发送数据,可以发布单个值或者多个值。
let publisher = [1, 2, 3].publisher
比如网络请求结果、用户输入、定时器触发等场景,都可以通过Publisher发布值。
Publisher有多种类型(比如Just、PassthroughSubject、CurrentValueSubject),更多知识点请见《Combine发布者Publisher》。
2、Operator(操作符)
操作符是发布者和订阅者之间的加工厂,可以使用map、filter等方法加工发布的数据。
Just(5)
.map { $0 * 2 } // 乘以 2
.filter { $0 % 2 == 0 } // 只要偶数
.sink { print($0) } // 输出:10
map和filter是Operator,加工发布的数据,更多操作符请见《Combine操作符Operator》。
3、Subscriber(订阅者)
订阅者负责接收数据,通常使用 .sink 创建订阅者。
let subscriber = publisher.sink { value in
print("Received value: \(value)")
}
除了 sink,还可以使用 onReceive,详细请见《Combine订阅者Subscriber》。
Combine链可以理解为:发布者发布值后,操作符加工值,最后返回给订阅者。
Combine也可以取消,当不再订阅时,可以通过 .cancel() 停止订阅,避免内存溢出。
Combine和SwiftUI
SwiftUI中的@State、@Binding、@ObservedObject、@Published等属性包装器,都和Combine存在联系。
SwiftUI视图本质上是在“订阅状态变化”,当状态发生变化时,重新绘制视图,Combine提供了这套变化传播的底层机制。
class CounterViewModel: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}
SwiftUI默认隐式依赖Combine,在使用这些属性包装器时,即使不导入Combine,也可以直接编译。
总结
Combine框架通过“发布者-订阅者”模式,解决了传统异步编程中的回调地狱、错误处理分散、线程管理复杂等问题。
本文的Publisher、Operator和Subscribe因为内容较多,被拆分到具体的文章中,需要深入理解才能掌握Combine。
Swift标准库还有多种类型被扩展为Combine的Publisher,比如Timer、NotificationCenter等等,具体请见《Swift常见的支持Publisher响应式处理的类或场景》。
