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: ")! 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.Multicast
和
Timer.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
過程中可能會遭遇的問題。如果不瞭解這些
“坑”
就開始上路,犯錯的機率會非常高,犯錯的成本也會非常高。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69984138/viewspace-2724132/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Combine 框架,從0到1 —— 2.通過 ConnectablePublisher 控制何時釋出框架
- Combine 框架,從0到1 —— 3.使用 Subscriber 控制釋出速度框架
- Combine 框架,從0到1 —— 5.Combine 提供的釋出者(Publishers)框架
- Combine 框架,從0到1 —— 1.核心概念框架
- Combine 框架,從0到1 —— 4.在 Combine 中使用計時器框架
- Combine 框架,從0到1 —— 5.Combine 中的 Subjects框架
- Combine 框架,從0到1 —— 4.在 Combine 中使用 KVO框架
- Combine 框架,從0到1 —— 4.在 Combine 中使用通知框架
- Combine 框架,從0到1 —— 5.Combine 常用操作符框架
- Combine 框架,從0到1 —— 4.在 Combine 中執行非同步程式碼框架非同步
- iOS 從0到1搭建高可用App框架iOSAPP框架
- iOS 從0到1搭建高可用App框架(二)iOSAPP框架
- 詳解從 0 釋出 react 元件到 npm 上React元件NPM
- APP 開發從 0 到 1(二)框架與網路APP框架
- 從0到1構建策略卡牌養成框架框架
- [譯] Flutter 從 0 到 1Flutter
- 從 0 到 1 認識 TypescriptTypeScript
- 從0到1實現PromisePromise
- Android Router 從 0 到 1Android
- 0到1,Celery從入門到出家
- [打包優化]從0到1搭建element後臺框架優化篇優化框架
- 如何從0到1設計一個類Dubbo的RPC框架RPC框架
- 從0到1開發一個小程式cli腳手架(二)--版本釋出/管理篇
- 學習seo如何從0到1
- gtest學習教程(從0到1)
- webpack從0到1使用指南Web
- 24小時從0到1開發陰陽師小程式
- 從0到1搭建一款Vue可配置視訊播放器元件(Npm已釋出)Vue播放器元件NPM
- selenium 資料驅動框架自動化從0到1--3框架
- 從0到1,開發一個動畫庫(1)動畫
- DNSLOG平臺搭建從0到1DNS
- 從0到1,小白的前端摸索之路前端
- 從0到1實現VueUI庫思路VueUI
- Android輪播圖從0到1Android
- 從0到1進行Spark history分析Spark
- 從0到1搭建自助分析平臺
- 從0到1使用kubebuiler開發operatorUI
- node專案從0到1實戰