本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 —— 2.通过 ConnectablePublisher 控制何时发布

 

内容概览

  • 前言
  • 使用 makeConnectable() 和 connect() 手动控制发布
  • 使用 autoconnect() 操作符进行自动连接
  • 总结

 

前言

 

使用 Connectable Publisher, 你可以决定发布者何时开始发送订阅元素给订阅者。那么,为什么我们需要这么做?

使用 sink(receiveValue:) 可以立刻开始接收订阅元素,但是这可能不是你想要的结果。当多个订阅者订阅了同一个发布者时,有可能会出现其中一个订阅者收到订阅内容,而另外一个订阅者收不到的情况。

比如,当你发起一个网络请求,并为这个请求创建了一个发布者以及连接了这个发布者的订阅者。

然后,这个订阅者的订阅操作触发了实际的网络请求。在某个时间点,你将第二个订阅者连接到了这个发布者。如果在连接第二个订阅者之前,网络请求已经完成,那么第二个订阅者将只会收到完成事件,收不到网络请求的响应结果。这时候,这个结果将不是你所期望。

在使用 Combine 的过程中,我们往往需要面对这些问题。现在就来弄清楚如何处理这一类问题吧~

 

使用 makeConnectable() 和 connect() 控制发布

 

ConnectablePublisher 是一个协议类型,它可以在你准备好之前阻止发布者发布元素。

/// 可连接的发布者,它提供了显式的连接、取消订阅的方式
///
/// 使用 `makeConnectable()` 来从任何一个失败类型是 `Never` 的发布者创建一个 `ConnectablePublisher`
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public protocol ConnectablePublisher : Publisher { /// 连接到发布者并返回一个用于取消发布的 `Cancellable` 实例
///
/// - 返回值: 一个用于取消发布的 `Cancellable` 实例
func connect() -> Cancellable
}

在你显式地调用 connect() 方法之前,一个 ConnectablePublisher 不会发送任何元素。

现在,就让我们用 ConnectablePublisher 来解决上面提到的网络请求示例中的问题吧!

在两个订阅者都连接到发布者之后,调用 connect(),然后网络请求才被触发。这样就可以避免竞争(race condition),保证两个订阅者都收到数据。

为了在你的 Combine 代码中使用 ConnectablePublisher,你可以使用 makeConnectable() 操作符将当前的发布者包装到一个 Publishers.MakeConnectable 结构体实例中。

如下方的代码所示:

class ConnectablePublisherDemo {

    private var cancellable1: AnyCancellable?
private var cancellable2: AnyCancellable?
private var connection: Cancellable? func run() {
let url = URL(string: "https://ficow.cn")!
let connectable = URLSession.shared
.dataTaskPublisher(for: url)
.map(\.data)
.catch() { _ in Just(Data()) }
.share()
.makeConnectable() // 阻止发布者发布内容 cancellable1 = connectable
.sink(receiveCompletion: { print("Received completion 1: \($0).") },
receiveValue: { print("Received data 1: \($0.count) bytes.") }) DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.cancellable2 = connectable.sink(receiveCompletion: { log("Received completion 2: \($0).") },
receiveValue: { log("Received data 2: \($0.count) bytes.") })
} DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
// 显式地启动发布。返回值需要被强引用,可用于取消发布(主动调用cancel方法或返回值被析构)
self.connection = connectable.connect()
}
} }

请注意,在 makeConnectable() 操作符前面有一个 share() 操作符!请问,这个操作符有什么作用呢?

 

使用 autoconnect() 操作符进行自动连接

 

某些 Combine 发布者已经实现了 ConnectablePublisher 协议,如:Publishers.MulticastTimer.TimerPublisher。使用这些发布者时,如果你不需要配置发布者或者不需要连接多个订阅者,你就需要显式地调用 connect() 方法。

对于这种情况,ConnectablePublisher 提供了 autoconnect() 操作符。当一个订阅者通过 subscribe(_:) 方法连接到发布者时,connect() 方法会被马上调用。

let cancellable = Timer.publish(every: 1, on: .main, in: .default)
.autoconnect()
.sink() { date in
print ("Date now: \(date)")
}

上面的代码示例中使用了 autoconnect(),所以订阅者可以马上接收到定时器发送的元素。如果没有 autoconnect(),我们就需要在某个时刻手动地调用 connect() 方法。

 

总结

 

Combine 为我们提供了很强大的异步编程功能,不过这也是有代价的,我们需要深知使用 Combine 过程中可能会遭遇的问题。如果不了解这些“坑”就开始上路,犯错的概率会非常高,犯错的成本也会非常高。

 

本文内容来源: Controlling Publishing with Connectable Publishers,转载请注明出处

 

Combine 框架,从0到1 —— 2.通过 ConnectablePublisher 控制何时发布的更多相关文章

  1. Combine 框架,从0到1 —— 5.Combine 提供的发布者(Publishers)

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 5.Combine 提供的发布者(Publishers). 内容概览 前言 Just Future D ...

  2. Combine 框架,从0到1 —— 1.核心概念

      本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 1.核心概念.     内容概览 前言 核心概念 RxSwift Combine 总结 参考内容 ...

  3. Combine 框架,从0到1 —— 3.使用 Subscriber 控制发布速度

      本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 3.使用 Subscriber 控制发布速度.   内容概览 前言 在发布者生产元素时消耗它们 使 ...

  4. Combine 框架,从0到1 —— 4.在 Combine 中使用通知

      本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 4.在 Combine 中使用通知.   内容概览 前言 让通知处理代码使用 Combine 总结 ...

  5. Combine 框架,从0到1 —— 4.在 Combine 中使用计时器

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 4.在 Combine 中使用计时器. 内容概览 前言 使用计时器执行周期性的工作 将计时器转换为计时 ...

  6. Combine 框架,从0到1 —— 4.在 Combine 中使用 KVO

      本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 4.在 Combine 中使用 KVO.   内容概览 前言 用 KVO 监控改动 将 KVO 代 ...

  7. Combine 框架,从0到1 —— 4.在 Combine 中执行异步代码

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 4.在 Combine 中执行异步代码. 内容概览 前言 用 Future 取代回调闭包 用输出类型( ...

  8. Combine 框架,从0到1 —— 5.Combine 中的 Subjects

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 5.Combine 中的 Subjects. 内容概览 前言 PassthroughSubject C ...

  9. Combine 框架,从0到1 —— 5.Combine 常用操作符

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 5.Combine 常用操作符. 内容概览 前言 print breakpoint handleEve ...

随机推荐

  1. 痞子衡嵌入式:MCUXpresso IDE下使用J-Link下载算法在Flash调试注意事项(i.MXRT500为例)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是MCUXpresso IDE下使用J-Link下载算法在Flash调试注意事项. 痞子衡前段时间写过一篇小文<为i.MXRT设计更 ...

  2. 6.29 省选模拟赛 坏题 AC自动机 dp 图论

    考场上随手构造了一组数据把自己卡掉了 然后一直都是掉线状态了. 最后发现这个东西不是subtask -1的情况不多 所以就没管无解直接莽 写题有点晚 故没调出来.. 考虑怎么做 容易想到建立AC自动机 ...

  3. 洛谷3月月赛div2 题解(模拟+数学+贪心+数学)

    由于本人太蒻了,div1的没有参加,胡乱写了写div2的代码就赶过来了. T1 苏联人 题目背景 题目名称是吸引你点进来的. 这是一道正常的题,和苏联没有任何关系. 题目描述 你在打 EE Round ...

  4. 编程与算法(一)、C语言实现二分法(方程近似解)

    一.二分法 假设有这样一个函数f(x) 函数与x轴有一个交点(也就是f(a)*f(b)<0,a<b),现在我们要求这个点的x值,也就是方程f(x)=0的一个实根 直接解显然不合适,那么接下 ...

  5. Weighted-Residual-Connections

  6. 【计算机算法设计与分析】——NP

    时间复杂度 时间复杂度并不是表示一个程序解决问题需要花多少时间,而是当问题规模扩大后,程序需要的时间长度增长得有多快.也就是说,对于高速处理数据的计算机来说,处理某一个特定数据的效率不能衡量一个程序的 ...

  7. 【小白学AI】XGBoost 推导详解与牛顿法

    文章转自公众号[机器学习炼丹术],关注回复"炼丹"即可获得海量免费学习资料哦! 目录 1 作者前言 2 树模型概述 3 XGB vs GBDT 3.1 区别1:自带正则项 3.2 ...

  8. java 网络通信协议、UDP与TCP

    一 网络通信协议 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定 的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样.在计算机网络中,这些连接和通 ...

  9. Unity3D制作类似吃鸡的小地图

    先看效果图: 实现的效果就是右上角的一个小地图,会随着人物的移动而移动,显示人物的方向,并且可以展示地图设定范围的其他的玩家 制作起来也很简单,不需要任何代码.主要原理就是先创建Render Text ...

  10. RoBERTa:一个调到最优参的BERT

    RoBERTa: A Robustly Optimized BERT Pretraining Approach. Yinhan Liu, Myle Ott, Naman Goyal, et al. 2 ...