看下 RxSwift 的双向绑定, 及 RxCocoa 的相关源代码

通常 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, bindRxCocoasubscribe 的封装,换句话,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, 就完了。

再看下, 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()
                }
     // 上面是内存管理,避免循环引用的 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")
        }
    }


...
复制代码
相关文章
相关标签/搜索