在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
赂弓。注意state
的subscribe
方法中是直接調(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
指代)赤套。
第三步,建立EM
與replaySubject
和reduce
的聯(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
兼都,這里的O
為ObservableSchedulerContext<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
將指定的的owner
和replaySubject
通過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)
生成EM1
,EM1
通過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))