RxFeedback 粗略源碼分析

在RxSwift文檔中,介紹了它的常用框架,現(xiàn)在就來看看其中之一RxFeedback棺耍,至于用法與優(yōu)勢(shì)可以查看文檔中的介紹虱咧,現(xiàn)在通過源碼來分析其中狀態(tài)與事件如何聯(lián)系起來的熊榛。在這個(gè)框架中主要成分有State、Event腕巡、Mutation玄坦,然后通過reduce(加工程序)和feedback(連接管理)把它們串聯(lián)起來形成一個(gè)反饋系統(tǒng)。一個(gè)反饋就是:事件(Event)發(fā)出指令(Mutation),通過加工程序處理煎楣,更新狀態(tài)(State)豺总,反饋到狀態(tài)的訂閱者。事件序列的創(chuàng)建和狀態(tài)的訂閱在feedback中完成择懂。為了方便喻喳,在下文中,面對(duì)于反饋系統(tǒng)進(jìn)行的輸入反饋處理的載體稱為客戶環(huán)境休蟹。

先來看看系統(tǒng)創(chuàng)建的主要源碼:

 public static func system<State, Mutation>(
            initialState: State,
            reduce: @escaping (State, Mutation) -> State,
            scheduler: ImmediateSchedulerType,
            scheduledFeedback: [Feedback<State, Mutation>]
        ) -> Observable<State> {
        return Observable<State>.deferred {
            let replaySubject = ReplaySubject<State>.create(bufferSize: 1)

            let asyncScheduler = scheduler.async
            
            let mutations: Observable<Mutation> = Observable.merge(scheduledFeedback.map { feedback in
                let state = ObservableSchedulerContext(source: replaySubject.asObservable(), scheduler: asyncScheduler)
                return feedback(state)
            })
            // This is protection from accidental ignoring of scheduler so
            // reentracy errors can be avoided
            .observeOn(CurrentThreadScheduler.instance)

            return mutations.scan(initialState, accumulator: reduce)
                .do(onNext: { output in
                    replaySubject.onNext(output)
                }, onSubscribed: {
                    replaySubject.onNext(initialState)
                })
                .subscribeOn(scheduler)
                .startWith(initialState)
                .observeOn(scheduler)
        }
    }

第一步沸枯,是創(chuàng)建State的可觀察序列replaySubject,其后通過ObservableSchedulerContext進(jìn)行包裝為state赂弓。注意statesubscribe方法中是直接調(diào)用的自身source的訂閱方法return self.source.subscribe(observer)绑榴,所以后續(xù)訂閱state對(duì)象就是直接訂閱replaySubject

第二步盈魁, feedback(state)翔怎,在這個(gè)方法中處理了兩件事情,一個(gè)是通過這個(gè)方法傳入state讓其能夠在客戶環(huán)境中被訂閱杨耙,二是在客戶環(huán)境中創(chuàng)建包含指定指令事件序列mutations(在下文中用EM指代)赤套。

第三步,建立EMreplaySubjectreduce的聯(lián)系珊膜,mutations.scan(initialState, accumulator: reduce)容握,首先使用提供的reduce加工處理狀態(tài)State,然后在EM的Observer中將處理后的State傳遞到replaySubject在客戶環(huán)境的訂閱者replaySubject.onNext(output)车柠。

再來看看feedback的原型Feedback剔氏,它的定義為public typealias Feedback<State, Mutation> = (ObservableSchedulerContext<State>) -> Observable<Mutation>,輸入為ObservableSchedulerContext<State>replaySubject)竹祷,輸出為Observable<Mutation>EM)的函數(shù)谈跛。在Feedbacks.swift文件中提供了多種創(chuàng)建方法,現(xiàn)在參考文檔中Counter示例塑陵,分析其中的一種感憾。示例主體代碼為:

        typealias State = Int

        enum Mutation {
            case increment
            case decrement
        }

        Observable.system(
            initialState: 0,
            reduce: { (state, mutation) -> State in
                switch mutation {
                case .increment:
                    return state + 1
                case .decrement:
                    return state - 1
                }
        },
            scheduler: MainScheduler.instance,
            scheduledFeedback:
                // UI is user feedback
                bind(self) { me, state -> Bindings<Mutation> in
                    let subscriptions = [
                        state.map(String.init).bind(to: me.label!.rx.text)
                    ]

                    let mutations = [
                        me.plus!.rx.tap.map { Mutation.increment },
                        me.minus!.rx.tap.map { Mutation.decrement }
                    ]

                    return Bindings(subscriptions: subscriptions,
                                    mutations: mutations)
                }
            )
            .subscribe()
            .disposed(by: disposeBag)

其中bind(self) {...調(diào)用的方法為:

public func bind<State, Mutation, WeakOwner>(_ owner: WeakOwner, _ bindings: @escaping (WeakOwner, ObservableSchedulerContext<State>) -> (Bindings<Mutation>))
    -> (ObservableSchedulerContext<State>) -> Observable<Mutation> where WeakOwner: AnyObject {
        return bind(bindingsStrongify(owner, bindings))
}

客戶環(huán)境傳入的閉包為bindings,是描述為 (WeakOwner, ObservableSchedulerContext<State>) -> (Bindings<Mutation>)的閉包令花,后面統(tǒng)稱為閉包A阻桅。
然后通過下面的代碼bindingsStrongify(owner, bindings)處理返回一個(gè)(O) -> (Bindings<Mutation>)的閉包,后面統(tǒng)稱為閉包B兼都,這里的OObservableSchedulerContext<State>鳍刷,相比于閉包A去除了owner。

private func bindingsStrongify<Mutation, O, WeakOwner>(_ owner: WeakOwner, _ bindings: @escaping (WeakOwner, O) -> (Bindings<Mutation>))
    -> (O) -> (Bindings<Mutation>) where WeakOwner: AnyObject {
    return { [weak owner] state -> Bindings<Mutation> in
        guard let strongOwner = owner else {
            return Bindings(subscriptions: [], mutations: [Observable<Mutation>]())
        }
        return bindings(strongOwner, state)
    }
}

最后通過下面代碼俯抖,返回一個(gè)閉包(ObservableSchedulerContext<State>) -> Observable<Mutation>則是符合Feedback的閉包输瓜,稱為閉包C,作為輸入傳入到Observable.system方法。

public func bind<State, Mutation>(_ bindings: @escaping (ObservableSchedulerContext<State>) -> (Bindings<Mutation>)) -> (ObservableSchedulerContext<State>) -> Observable<Mutation> {
    return { (state: ObservableSchedulerContext<State>) -> Observable<Mutation> in
        return Observable<Mutation>.using({ () -> Bindings<Mutation> in
            return bindings(state)
        }, observableFactory: { (bindings: Bindings<Mutation>) -> Observable<Mutation> in
            return Observable<Mutation>
                    .merge(bindings.mutations)
                    .concat(Observable.never())
                    .enqueue(state.scheduler)
        })
    }
}

調(diào)用過程尤揣,在系統(tǒng)生成的步驟二中搔啊,調(diào)用feedback(state),將前面的replaySubject傳入到閉包C北戏, 通過閉包C 中的bindings(state)replaySubject傳入到閉包B负芋,閉包B將指定的的ownerreplaySubject通過bindings(strongOwner, state)傳入到客戶環(huán)境閉包A中。這就是State關(guān)聯(lián)序列的傳入到客戶環(huán)境的過程嗜愈。在閉包A中客戶環(huán)境訂閱State序列replaySubject接收反饋旧蛾。再來分析EM的創(chuàng)建和訂閱,通過客戶環(huán)境閉包A的執(zhí)行蠕嫁,創(chuàng)建了相關(guān)于Mutation的事件序列組锨天,作為返回對(duì)象Bindings的成員,在閉包C中的Observable<Mutation>.using方法剃毒,將resourceFactory創(chuàng)建的Bindings對(duì)象(調(diào)用bindings(state)生成的)作為輸入傳入到observableFactory病袄,observableFactory進(jìn)行Mutation關(guān)聯(lián)的事件序列組的merge處理Observable<Mutation>.merge(bindings.mutations)生成EM1EM1通過using處理生成EM2赘阀,EM2返回到系統(tǒng)生成中益缠,通過再次與其他feedback(state)生成的EMn進(jìn)行merge處理生成了最終的EM,然后系統(tǒng)進(jìn)入步驟三完成最后的關(guān)聯(lián)基公。

另一些小積累:
Observable<Mutation>.using 的 resourceFactory輸出作為observableFactory的輸入在源碼Using.swift

           let resource = try _parent._resourceFactory()
           disposable = resource
           let source = try _parent._observableFactory(resource)

幅慌,另外source.subscribe(self)將自己作為source的訂閱者,分發(fā)source的事件轰豆。所以胰伍,using的訂閱者獲取的事件都是來自source。對(duì)于using中source序列完成后秒咨,摧毀resource可通過下面的代碼可見:

···            
  return Disposables.create(
                source.subscribe(self),
                disposable
            )
···

  case .completed:
            forwardOn(.completed)
            dispose()

喇辽,對(duì)于在RxFeedback中Binding對(duì)象中replaySubject訂閱返回的對(duì)象的摧毀掌挚,是由于返回的Binding對(duì)象是支持Disposable協(xié)議的雨席,在它的實(shí)現(xiàn)方法中,對(duì)訂閱返回對(duì)象進(jìn)行摧毀吠式,如下

 public func dispose() {
        for subscription in subscriptions {
            subscription.dispose()
        }
    }

陡厘,整個(gè)調(diào)用過程為:using中的resource發(fā)出完成命令,調(diào)用了dispose()進(jìn)行釋放特占,釋放對(duì)象的disposable為Binding對(duì)象(disposable = resource)糙置,Binding對(duì)象調(diào)用dispose()方法,逐個(gè)調(diào)用訂閱返回對(duì)象的摧毀方法是目。則也可以看出using如何摧毀resource的谤饭。(這里的resource為Binding對(duì)象)

Scan新的State狀態(tài)的保存過程,源碼在Scan.swift,注意_accumulate前面的&

     try _parent._accumulator(&_accumulate, element)
     forwardOn(.next(_accumulate))
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末揉抵,一起剝皮案震驚了整個(gè)濱河市亡容,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冤今,老刑警劉巖闺兢,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異戏罢,居然都是意外死亡屋谭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門龟糕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桐磁,“玉大人,你說我怎么就攤上這事翩蘸∷猓” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵催首,是天一觀的道長(zhǎng)扶踊。 經(jīng)常有香客問我,道長(zhǎng)郎任,這世上最難降的妖魔是什么秧耗? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮舶治,結(jié)果婚禮上分井,老公的妹妹穿的比我還像新娘。我一直安慰自己霉猛,他們只是感情好尺锚,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惜浅,像睡著了一般瘫辩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坛悉,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天伐厌,我揣著相機(jī)與錄音,去河邊找鬼裸影。 笑死挣轨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的轩猩。 我是一名探鬼主播卷扮,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼荡澎,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了晤锹?” 一聲冷哼從身側(cè)響起衔瓮,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抖甘,沒想到半個(gè)月后热鞍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衔彻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年薇宠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艰额。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澄港,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柄沮,到底是詐尸還是另有隱情回梧,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布祖搓,位于F島的核電站狱意,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拯欧。R本人自食惡果不足惜详囤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望镐作。 院中可真熱鬧藏姐,春花似錦、人聲如沸该贾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杨蛋。三九已至兜材,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間六荒,已是汗流浹背护姆。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工矾端, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留掏击,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓秩铆,卻偏偏與公主長(zhǎng)得像砚亭,于是被迫代替她去往敵國(guó)和親灯变。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理捅膘,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,639評(píng)論 18 139
  • Introduction: Creating and Subscribing to Observables: Th...
    loongod閱讀 737評(píng)論 0 0
  • 本篇文章介主要紹RxJava中操作符是以函數(shù)作為基本單位匆浙,與響應(yīng)式編程作為結(jié)合使用的昧穿,對(duì)什么是操作、操作符都有哪些...
    嘎啦果安卓獸閱讀 2,853評(píng)論 0 10
  • 最近在學(xué)習(xí)RxSwift相關(guān)的內(nèi)容署尤,在這里記錄一些基本的知識(shí)點(diǎn)耙替,以便今后查閱。 Observable 在RxSwi...
    L_Zephyr閱讀 1,748評(píng)論 1 4
  • 愛情中我們應(yīng)該是學(xué)會(huì)一些小技巧曹体,那樣才會(huì)讓我們得到自己喜歡的人俗扇。那么假如你喜歡一個(gè)人的時(shí)候你怕自己過于的主動(dòng),讓對(duì)...
    秋舞閱讀 227評(píng)論 0 0