RxSwift PublishSubject 分析

PublishSubject繼承類和協(xié)議

public final class PublishSubject<Element>
    : Observable<Element>
    , SubjectType
    , Cancelable
    , ObserverType
    , SynchronizedUnsubscribeType


  • Observable: 被觀察者

  • Cancelable: 可以被dispose

  • SynchronizedUnsubscribeType 可以取消訂閱

  • ObserverType: 觀察者

  • SubjectType:

    1. asObserver
    2. subscribe

這個協(xié)議很好的描述對象即可以作為Observer ,也可以作為 Observerable 的特性

Observers Type

要理解PublishSubject的核心朝抖,得先理解Observers 這個類型

typealias Observers = AnyObserver<Element>.s

extension AnyObserver {
    /// Collection of `AnyObserver`s
    typealias s = Bag<(Event<Element>) -> ()>
}

about Bag

Bag: 是一個自定義容器般妙,類似于Dictionary饭望, 之所以自定義實現(xiàn)主要是出于性能考慮蜗字,有興趣的可以研究下蛤铜,這里先留個坑

總的來說Observers 是一個 Bag 容器, 元素的類型為 (Event<Element>) -> ()

訂閱

    public override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
        _lock.lock()
        let subscription = _synchronized_subscribe(observer)
        _lock.unlock()
        return subscription
    }

    func _synchronized_subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == E {
        if let stoppedEvent = _stoppedEvent {
            observer.on(stoppedEvent)
            return Disposables.create()
        }
        
        if _isDisposed {
            observer.on(.error(RxError.disposed(object: self)))
            return Disposables.create()
        }
        
        let key = _observers.insert(observer.on)
        return SubscriptionDisposable(owner: self, key: key)
    }


一個同步鎖婆殿, 幾個異常處理诈乒, 剩下的就是核心代碼:
let key = _observers.insert(observer.on) : 將 Observer.on 函數(shù)塞入Bag容器

return SubscriptionDisposable(owner: self, key: key)
返回SubscriptionDisposable

struct SubscriptionDisposable<T: SynchronizedUnsubscribeType> : Disposable {
    private let _key: T.DisposeKey
    private weak var _owner: T?

    init(owner: T, key: T.DisposeKey) {
        _owner = owner
        _key = key
    }

    func dispose() {
        _owner?.synchronizedUnsubscribe(_key)
    }
}


SubscriptionDisposable 功能比較簡單罩扇,只是對其進行簡單的封裝婆芦,那么這里為什么不讓PublishSubject 直接支持Disposable協(xié)議呢怕磨?這是為了保證設(shè)計的一致性,只有被訂閱的Observable才能被dispose, 如果讓PublishSubject直接支持Disposable協(xié)議就有損這個設(shè)計原則消约,固這里選擇返回SubscriptionDisposable

on 事件

    public func on(_ event: Event<Element>) {
        #if DEBUG
            _synchronizationTracker.register(synchronizationErrorMessage: .default)
            defer { _synchronizationTracker.unregister() }
        #endif
        dispatch(_synchronized_on(event), event)
    }

    func _synchronized_on(_ event: Event<E>) -> Observers {
        _lock.lock(); defer { _lock.unlock() }
        switch event {
        case .next(_):
            if _isDisposed || _stopped {
                return Observers()
            }
            
            return _observers
        case .completed, .error:
            if _stoppedEvent == nil {
                _stoppedEvent = event
                _stopped = true
                let observers = _observers
                _observers.removeAll()
                return observers
            }

            return Observers()
        }
    }


@inline(__always)
func dispatch<E>(_ bag: Bag<(Event<E>) -> ()>, _ event: Event<E>) {
    if bag._onlyFastPath {
        bag._value0?(event)
        return
    }

    let value0 = bag._value0
    let dictionary = bag._dictionary

    if let value0 = value0 {
        value0(event)
    }

    let pairs = bag._pairs
    for i in 0 ..< pairs.count {
        pairs[i].value(event)
    }

    if let dictionary = dictionary {
        for element in dictionary.values {
            element(event)
        }
    }
}


這段邏輯復(fù)雜一點肠鲫,先講一下Bag容器

  • 數(shù)據(jù)量為1 的時候 使用,直接使用value0 存儲元素
  • 數(shù)據(jù)量在(1,30)的時候或粮, 使用 ContiguousArray 存儲元素导饲, value0 仍然存儲0號元素
  • 超過30的數(shù)據(jù),使用Dictionary 存儲超出的元素

理解了這些就明白dispatch 干了些啥了氯材, 簡單說就是

 Bag.foreach { $0(event) }

一個PublishSubject 可以被多次訂閱渣锦,每次訂閱的時候?qū)?code>Observer.on 塞入Bag容器,在執(zhí)行PublishSubject.on 事件的時候氢哮, 會執(zhí)行所有Observeron 事件袋毙。

Unsubscribe

    func synchronizedUnsubscribe(_ disposeKey: DisposeKey) {
        _lock.lock()
        _synchronized_unsubscribe(disposeKey)
        _lock.unlock()
    }

    func _synchronized_unsubscribe(_ disposeKey: DisposeKey) {
        _ = _observers.removeKey(disposeKey)
    }

這個就很簡單,unsubscribe的時候冗尤,將ObserverBag容器中移除就可以听盖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市裂七,隨后出現(xiàn)的幾起案子皆看,更是在濱河造成了極大的恐慌,老刑警劉巖背零,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腰吟,死亡現(xiàn)場離奇詭異,居然都是意外死亡捉兴,警方通過查閱死者的電腦和手機蝎困,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倍啥,“玉大人禾乘,你說我怎么就攤上這事∷渎疲” “怎么了始藕?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長氮趋。 經(jīng)常有香客問我伍派,道長,這世上最難降的妖魔是什么剩胁? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任诉植,我火速辦了婚禮,結(jié)果婚禮上昵观,老公的妹妹穿的比我還像新娘晾腔。我一直安慰自己舌稀,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布灼擂。 她就那樣靜靜地躺著壁查,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剔应。 梳的紋絲不亂的頭發(fā)上睡腿,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機與錄音峻贮,去河邊找鬼席怪。 笑死,一個胖子當(dāng)著我的面吹牛纤控,可吹牛的內(nèi)容都是我干的何恶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼嚼黔,長吁一口氣:“原來是場噩夢啊……” “哼细层!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起唬涧,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤疫赎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后碎节,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捧搞,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年狮荔,在試婚紗的時候發(fā)現(xiàn)自己被綠了胎撇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡殖氏,死狀恐怖晚树,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情雅采,我是刑警寧澤爵憎,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站婚瓜,受9級特大地震影響宝鼓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巴刻,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一愚铡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胡陪,春花似錦沥寥、人聲如沸正蛙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至愚隧,卻和暖如春蒂阱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背狂塘。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工录煤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人荞胡。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓妈踊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親泪漂。 傳聞我的和親對象是個殘疾皇子廊营,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • (一)前言 前兩篇文章分析了RxSwift的整個基礎(chǔ)的訂閱流程以及變換操作(Operators)的概念實現(xiàn),有興趣...
    Maru閱讀 1,247評論 3 8
  • 為什么要用Rx 傳統(tǒng)的編程方式大多都是告訴程序需要做什么萝勤、怎么做露筒、什么時候做,并通過KVO敌卓、Notificatio...
    康富貴閱讀 1,796評論 1 2
  • 引入依賴: implementation 'io.reactivex.rxjava2:rxandroid:2.0....
    為夢想戰(zhàn)斗閱讀 1,304評論 0 0
  • 玉鐲作為飾品慎式,最早出現(xiàn)于新石器時代,已有近萬年的歷史趟径。而翡翠手鐲則始于明朝洪武年間瘪吏,清末及民國時期已盛行。 ...
    明華軒_何閱讀 672評論 0 3
  • 新的生活就這樣開始了蜗巧,之前訂的計劃全部擱淺了… 實驗室與食堂掌眠,項目與Bug,重復(fù)的日常生活… 我在想我到底有沒有真...
    e142444b2553閱讀 87評論 0 0