Swift Combine 之 Publisher數據流

Publisher

  • 發布源協議,可以實現該協議來實現自己的數據,
    • Subject繼承自Publisher,提供了三套默認的內置實現類
  • 容器包裝類,具體實現交由Subscriber實現類來轉發數據流
  • 內置提供各種各樣的操作符(函數式編程的仿函數,Swift語言的Operator)
    • allSatisfy
    • tryAllSatisfy
    • compactMap
    • contains
    • filter
    • tryFilter
    • throttle
    • ....等等
  • Publisher接口
    public protocol Publisher {
    /// 數據輸出流,相當于訂閱者的數據輸入流        
    associatedtype Output
    /// 數據發布,要么發布一個真實數據流,要么發布一個錯誤(或者可以選擇丟棄錯誤,      Never忽略錯誤) 
    associatedtype Failure: Error
 
     /// 接收數據源輸入流,并轉發給訂閱者
    func receive<Subscriber: OpenCombine.Subscriber>(subscriber: Subscriber) where Failure == Subscriber.Failure, Output ==       Subscriber.Input
  }
  • 所有的操作符的流程都類似,追蹤一個操作符的調用順序和觸發流程
  • 定義一個數組的數據流 [1, 2, 10000].publisher, publisherSequence的一個擴展,內部使用Publishers.Sequence進行了包裝成了一個可以被觀察的數據流
 extension Sequence {
     public var publisher: Publishers.Sequence<Self, Never> {
         return .init(sequence: self)
     }
- `Publishers.Sequence` 內部實現
 /// 序列流繼承于`Publisher` 
 public struct Sequence<Elements: Swift.Sequence, Failure: Error>: Publisher {
         /// 輸出源
         public typealias Output = Elements.Element
 
         public let sequence: Elements
 
         public init(sequence: Elements) {
             self.sequence = sequence
         }
         /// 實現 `Publisher`協議方法`receive`
         public func receive<Downstream: Subscriber>(subscriber: Downstream)
             where Failure == Downstream.Failure,
                   Elements.Element == Downstream.Input
         {
             /// 包裝類`Inner`實現了`Subscription`協議,內部持有當前收到的`subscriber`, 進行轉發
             let inner = Inner(downstream: subscriber, sequence: sequence)
 
             /// 判斷是否序列是否到末尾,如果序列結束發送完成事件并取消序列,數據流完成,反之,持續接收數據流
             if inner.isExhausted {
                 subscriber.receive(subscription: Subscriptions.empty)
                 subscriber.receive(completion: .finished)
                 inner.cancel()
             } else {
                 /// 內部會調用`Subscription`協議的 `request`方法 
                 subscriber.receive(subscription: inner)
             }
         }
     }
  • Inner作為一個私有類,單獨實現了序列數據流的內部數據源的流轉(每一個操作符都有一套內部特有的Inner實現類),源碼我進行了部分簡化
  private final class Inner<Downstream: Subscriber, Elements: Sequence, Failure>
         : Subscription
         where Downstream.Input == Elements.Element,
               Downstream.Failure == Failure
     {
         
         typealias Iterator = Elements.Iterator
         typealias Element = Elements.Element
 
         private var sequence: Elements?
         private var downstream: Downstream?
         private var iterator: Iterator
         private var next: Element?
         private var pendingDemand = Subscribers.Demand.none
 
         /// 初始化持有的`downstream`數據流,方便后續數據流轉
         fileprivate init(downstream: Downstream, sequence: Elements) {
             self.sequence = sequence
             self.downstream = downstream
             self.iterator = sequence.makeIterator()
             next = iterator.next()
         }
         
         func request(_ demand: Subscribers.Demand) {
             guard downstream != nil else {
                 return
             }
 
             while let downstream = self.downstream, pendingDemand > 0 {
                 if let current = self.next {
                     /// 迭代數據流,依次進行數據的轉發,交給訂閱者接收
                     let additionalDemand = downstream.receive(current)
                 }
 
                 if next == nil {
                     self.downstream = nil
                     self.sequence = nil
                     /// 序列結束,發送完成事件
                     downstream.receive(completion: .finished)
                     return
                 }
             }
         }
     }
  • 上面定義了數據流源,并發出了數據,等待訂閱者監聽數據流,sinkassign操作符可以進行訂閱,后續會列出sink和assign的源碼

代碼演示片段

         class Root: NSObject {
             var name: String = ""
         }
         
         let root = Root()
         
         let arr: [Int] = [1, 2, 100]
         
         /// 將數組轉換成一個數據流
         arr.publisher
             /// 過濾數據流中大于2的元素
             .filter{$0 > 2}
             /// 進行一次轉換,轉成String類型
             .compactMap{"\($0)"}
             /// `Sink`訂閱數據源
             .sink { value in
                 debugPrint("數據流: \(value)")
             }.store(in: &cancel)
         
          /// 使用keypath進行賦值
         arr.publisher
             .filter{$0 > 2}
             .compactMap{"\($0)"}
             /// `Assign` keypath 賦值
             .assign(to: \.name, on: root).store(in: &cancel)
         
         debugPrint("root name: \(root.name)")
 
        /// 控制臺輸出
        "數據流: 100"
        "root name: 100"
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容