看下 RxSwift 的双向绑定, 及 RxCocoa 的相关源代码
一般 RxSwift 用于 MVVM, MVVM 常用功能就是双向绑定,Model 和 UI 的相互数据关联。
看下官方的 <->
在 RxSwift 的案例代码中,有一个 Operators.swift
文件,提供了一个 <->
双向绑定操作符函数。
func <-> (property: ControlProperty, relay: BehaviorRelay) -> 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
对 subscribe
的封装,换句话,UI 订阅了模型的事件。
property.subscribe
, 模型订阅了 UI 的事件。
就这样,双向绑定完成了。
为什么不会陷入事件循环?
打个比方,如下代码,模型与 UI 绑定,点击按钮改模型,给模型传入一个事件。
(textFieldOne.rx.text <-> messageVal).disposed(by: disposeBag)
btn.rx.tap.subscribe(onNext: { (_) in
self.messageVal.accept("Go")
}).disposed(by: disposeBag)
模型 messageVal 收到一个事件,传给 UI textFieldOne.rx.text
, textFieldOne.rx.text
UI 传给模型 messageVal,模型 messageVal 再次传给 UI ...
实际上是没有死循环的。
可以简单理解为 textFieldOne.rx.text
做了保护。
下面是 UITextField+Rx.swift
的源代码。
extension Reactive where Base: UITextField {
/// Reactive wrapper for `text` property.
public var text: ControlProperty {
return value
}
/// Reactive wrapper for `text` property.
public var value: ControlProperty {
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
, 是一个计算属性,是 value
起作用。
value
又是一个计算属性,计算属性就是方法( getter/ setter 函数),
直观的看到一个 if if textField.text != value {
, 这样不会老是要把 textField
拎出来写入。
起作用的是 base.rx.controlPropertyWithDefaultEvents
,
UIControl+Rx.swift
的源代码:
internal func controlPropertyWithDefaultEvents(
editingEvents: UIControl.Event = [.allEditingEvents, .valueChanged],
getter: @escaping (Base) -> T,
setter: @escaping (Base, T) -> Void
) -> ControlProperty {
return controlProperty(
editingEvents: editingEvents,
getter: getter,
setter: setter
)
}
结论: 这里只用到了 [.allEditingEvents, .valueChanged]
两种事件 UIControl.Event .
然后对 UIControl 建立 target action 机制,因为只有编辑和修改的控件事件,所以直接对 textField.text
赋值,是订阅不到的。
这样改模型 messageVal,模型 messageVal 收到一个事件,传给 UI textFieldOne.rx.text
, 就完了。
这样改UI textFieldOne.rx.text
,UI textFieldOne.rx.text
收到一个事件,传给 模型 messageVal,模型 messageVal 收到 UI 一个事件,再次传给 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(
editingEvents: UIControl.Event,
getter: @escaping (Base) -> T,
setter: @escaping (Base, T) -> Void
) -> ControlProperty {
// 处理 getter
let source: Observable = Observable.create { [weak weakControl = base] observer in
guard let control = weakControl else {
observer.on(.completed)
return Disposables.create()
}
// 上面是内存管理,避免循环引用的 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(values: source, valueSink: bindingObserver)
}
在 ControlTarget.swift 文件中,添加 target action 相对简单清晰
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 作为专业做双向绑定的,提供了三种场景,模型( 行为主体 )绑定模型,控件绑定控件,模型绑定控件。
// 控件绑定控件
public func <->(left: ControlProperty, right: ControlProperty) -> Disposable {}
// 模型绑定模型
public func <->(left: BehaviorRelay, right: BehaviorRelay) -> Disposable {}
// 模型绑定控件
public func <->(left: ControlProperty, right: BehaviorRelay) -> Disposable {}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
涉及的框架与服务,真是多啊。
WXApiService
// 微信服务UMSocialService
// 友盟相关TingyunAppService
// 听云sdk接入