本文首发于 Ficow Shen’s Blog,原文地址: Combine 框架,从0到1 —— 5.Combine 提供的发布者(Publishers)。
内容概览
- 前言
- Just
- Future
- Deferred
- Empty
- Publishers.Sequence
- Fail
- Record
- Share
- Multicast
- ObservableObject
- @Published
- 总结
前言
正所谓,工欲善其事,必先利其器。在开始使用 Combine
进行响应式编程之前,建议您先了解 Combine
为您提供的各种发布者(Publishers)、操作符(Operators)、订阅者(Subscribers)。合理地选择符合需求的 Combine
发布者,可以大幅度地提升您的开发效率!
这些都是 Combine
为我们提供的发布者:
Just
,Future
,Deferred
,Empty
,Publishers.Sequence
,Fail
,Record
,Share
,Multicast
,ObservableObject
,@Published
。
接下来的几分钟,让我们把它们各个击破!
请注意,后续内容中出现的 cancellables
全部由这个类的实例提供 :
final class CombinePublishersDemo {
private var cancellables = Set<AnyCancellable>()
}
示例代码 Github 仓库:CombinePublishersDemo
Just
Just
向每个订阅者只发送单个值,然后结束。它的失败类型为 Never
,也就是不能失败。 示例代码:
func just() {
Just(1) // 直接发送1
.sink {
value in
// 输出:just() 1
print(#function, value)
}
.store(in: &cancellables)
}
输出内容:
just() 1
Just
常被用在错误处理中,在捕获异常后发送备用值。 示例代码:
func just2() {
// 使用 Fail 发送失败
Fail(error: NSError(domain: "", code: 0, userInfo: nil))
.catch {
_ in
// 捕获错误,返回 Just(3)
return Just(3)
}
.sink {
value in
// 输出:just2() 3
print(#function, value)
}
.store(in: &cancellables)
}
输出内容:
just2() 3
Future
Future
使用一个闭包来进行初始化,最终这个闭包将执行传入的一个闭包参数(promise)来发送单个值或者失败。请不要使用 Future
发送多个值。PassthroughSubject
, CurrentValueSubject
或者 Deferred
会是更好的选择。
请注意,Future
不会等待订阅者发送需求,它会在被创建时就立刻异步执行这个初始化时传入的闭包!如果你需要等待订阅者发送需求时才执行这个闭包,请使用 Deferred
。如果你需要重复执行这个闭包,也请使用 Deferred
。
示例代码:
func future() {
Future<Int, Never> {
promise in
// 延时1秒
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
promise(.success(2))
}
}
.sink {
value in
// 输出:future() 2
print(#function, value)
}
.store(in: &cancellables)
}
输出内容:
future() 2
更常见的用法是将 Future
作为一个任务函数的返回值,让具体任务的执行代码与订阅代码分离:
private func bigTask() -> Future<Int, Error> {
return Future() {
promise in
// 模拟耗时操作
sleep(1)
guard Bool.random() else {
promise(.failure(NSError(domain: "com.ficowshen.blog", code: -1, userInfo: [NSLocalizedDescriptionKey: "task failed"])))
return
}
promise(.success(3))
}
}
func future2() {
bigTask()
.subscribe(on: DispatchQueue.global())
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {
completion in
switch completion {
case .finished:
// 输出:future2() finished
print(#function, "finished"