看下 RxSwift 的雙向綁定撵割, 及 RxCocoa 的相關(guān)源代碼
一般 RxSwift 用于 MVVM, MVVM 常用功能就是雙向綁定,Model 和 UI 的相互數(shù)據(jù)關(guān)聯(lián)辙芍。
看下官方的 <->
在 RxSwift 的案例代碼中啡彬,有一個(gè) Operators.swift
文件,提供了一個(gè) <->
雙向綁定操作符函數(shù)故硅。
func <-> <T>(property: ControlProperty<T>, relay: BehaviorRelay<T>) -> Disposable {
let bindToUIDisposable = relay.bind(to: property)
let bindToRelay = property
.subscribe(onNext: { n in
relay.accept(n)
}, onCompleted: {
bindToUIDisposable.dispose()
})
return Disposables.create(bindToUIDisposable, bindToRelay)
}
代碼邏輯很清晰庶灿,relay.bind(to: property)
, 模型綁定 UI, bind
是 RxCocoa
對(duì) subscribe
的封裝,換句話吃衅,UI 訂閱了模型的事件往踢。
property.subscribe
, 模型訂閱了 UI 的事件。
就這樣徘层,雙向綁定完成了峻呕。
為什么不會(huì)陷入事件循環(huán)?
打個(gè)比方趣效,如下代碼山上,模型與 UI 綁定,點(diǎn)擊按鈕改模型英支,給模型傳入一個(gè)事件佩憾。
(textFieldOne.rx.text <-> messageVal).disposed(by: disposeBag)
btn.rx.tap.subscribe(onNext: { (_) in
self.messageVal.accept("Go")
}).disposed(by: disposeBag)
模型 messageVal 收到一個(gè)事件,傳給 UI textFieldOne.rx.text
, textFieldOne.rx.text
UI 傳給模型 messageVal,模型 messageVal 再次傳給 UI ...
實(shí)際上是沒有死循環(huán)的妄帘。
可以簡(jiǎn)單理解為 textFieldOne.rx.text
做了保護(hù)楞黄。
下面是 UITextField+Rx.swift
的源代碼。
extension Reactive where Base: UITextField {
/// Reactive wrapper for `text` property.
public var text: ControlProperty<String?> {
return value
}
/// Reactive wrapper for `text` property.
public var value: ControlProperty<String?> {
return base.rx.controlPropertyWithDefaultEvents(
getter: { textField in
textField.text
},
setter: { textField, value in
// This check is important because setting text value always clears control state
// including marked text selection which is imporant for proper input
// when IME input method is used.
if textField.text != value {
textField.text = value
}
}
)
}
textFieldOne.rx.text
里面的 .text
, 是一個(gè)計(jì)算屬性抡驼,是 value
起作用鬼廓。
value
又是一個(gè)計(jì)算屬性,計(jì)算屬性就是方法( getter/ setter 函數(shù))致盟,
直觀的看到一個(gè) if if textField.text != value {
, 這樣不會(huì)老是要把 textField
拎出來寫入碎税。
起作用的是 base.rx.controlPropertyWithDefaultEvents
,
UIControl+Rx.swift
的源代碼:
internal func controlPropertyWithDefaultEvents<T>(
editingEvents: UIControl.Event = [.allEditingEvents, .valueChanged],
getter: @escaping (Base) -> T,
setter: @escaping (Base, T) -> Void
) -> ControlProperty<T> {
return controlProperty(
editingEvents: editingEvents,
getter: getter,
setter: setter
)
}
結(jié)論: 這里只用到了 [.allEditingEvents, .valueChanged]
兩種事件 UIControl.Event .
然后對(duì) UIControl 建立 target action 機(jī)制,因?yàn)橹挥芯庉嫼托薷牡目丶录笪灾苯訉?duì) textField.text
賦值雷蹂,是訂閱不到的。
這樣改模型 messageVal杯道,模型 messageVal 收到一個(gè)事件匪煌,傳給 UI textFieldOne.rx.text
, 就完了。
這樣改UI textFieldOne.rx.text
党巾,UI textFieldOne.rx.text
收到一個(gè)事件萎庭,傳給 模型 messageVal,模型 messageVal 收到 UI 一個(gè)事件,再次傳給 UI textFieldOne.rx.text
, 就完了齿拂。
再看下驳规, RxCocoa 是怎樣建立 Target Action 的
還是 UIControl+Rx.swift
文件
/// Creates a `ControlProperty` that is triggered by target/action pattern value updates.
///
/// - parameter controlEvents: Events that trigger value update sequence elements.
/// - parameter getter: Property value getter.
/// - parameter setter: Property value setter.
public func controlProperty<T>(
editingEvents: UIControl.Event,
getter: @escaping (Base) -> T,
setter: @escaping (Base, T) -> Void
) -> ControlProperty<T> {
// 處理 getter
let source: Observable<T> = Observable.create { [weak weakControl = base] observer in
guard let control = weakControl else {
observer.on(.completed)
return Disposables.create()
}
// 上面是內(nèi)存管理,避免循環(huán)引用的 weak strong dance, 下面開始做正事
observer.on(.next(getter(control)))
// 建立 target - action
let controlTarget = ControlTarget(control: control, controlEvents: editingEvents) { _ in
if let control = weakControl {
observer.on(.next(getter(control)))
}
}
return Disposables.create(with: controlTarget.dispose)
}
.takeUntil(deallocated)
// 處理 setter
let bindingObserver = Binder(base, binding: setter)
return ControlProperty<T>(values: source, valueSink: bindingObserver)
}
在 ControlTarget.swift 文件中署海,添加 target action 相對(duì)簡(jiǎn)單清晰
final class ControlTarget: RxTarget {
typealias Callback = (Control) -> Void
let selector: Selector = #selector(ControlTarget.eventHandler(_:))
weak var control: Control?
let controlEvents: UIControl.Event
var callback: Callback?
init(control: Control, controlEvents: UIControl.Event, callback: @escaping Callback) {
// 確保主線程
MainScheduler.ensureRunningOnMainThread()
// init 屬性
self.control = control
self.controlEvents = controlEvents
self.callback = callback
super.init()
// 添加 target action
control.addTarget(self, action: selector, for: controlEvents)
let method = self.method(for: selector)
if method == nil {
rxFatalError("Can't find method")
}
}
...
看下 RxBiBinding 源代碼达舒,
上正菜,RxBiBinding 源代碼
RxBiBinding 作為專業(yè)做雙向綁定的叹侄,提供了三種場(chǎng)景巩搏,模型( 行為主體 )綁定模型,控件綁定控件趾代,模型綁定控件贯底。
// 控件綁定控件
public func <-><E>(left: ControlProperty<E>, right: ControlProperty<E>) -> Disposable {}
// 模型綁定模型
public func <-><E>(left: BehaviorRelay<E>, right: BehaviorRelay<E>) -> Disposable {}
// 模型綁定控件
public func <-><E>(left: ControlProperty<E>, right: BehaviorRelay<E>) -> Disposable {}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
涉及的框架與服務(wù),真是多啊撒强。
WXApiService
// 微信服務(wù)UMSocialService
// 友盟相關(guān)TingyunAppService
// 聽云sdk接入