通常 RxSwift 用于 MVVM, MVVM 经常使用功能就是双向绑定,Model 和 UI 的相互数据关联。swift
<->
在 RxSwift 的案例代码中,有一个 Operators.swift
文件,提供了一个 <->
双向绑定操做符函数。api
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
对 subscribe
的封装,换句话,UI 订阅了模型的事件。bash
property.subscribe
, 模型订阅了 UI 的事件。app
就这样,双向绑定完成了。函数
打个比方,以下代码,模型与 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 ...spa
其实是没有死循环的。线程
能够简单理解为 textFieldOne.rx.text
作了保护。双向绑定
下面是 UITextField+Rx.swift
的源代码。code
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
, 是一个计算属性,是 value
起做用。 value
又是一个计算属性,计算属性就是方法( getter/ setter 函数),
直观的看到一个 if if textField.text != value {
, 这样不会总是要把 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
)
}
复制代码
[.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
, 就完了。
仍是 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()
}
// 上面是内存管理,避免循环引用的 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 相对简单清晰
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")
}
}
...
复制代码