美文网首页iOS点点滴滴
RxSwift源码探秘 :如何让UIControl生成可观察序列

RxSwift源码探秘 :如何让UIControl生成可观察序列

作者: ReevesGoo | 来源:发表于2017-11-19 16:16 被阅读0次

    我们在使用RxSwift进行响应式编程时,都会引入另一个库RxCocoa,RxCocoa是Rx对iOS的原生API中UIKit以及Foundation中的视图(UIView)、控制事件(Control Event)、键值观察(KVO)、通知(Notification)等的扩展,以便在开发时更方便的对系统的这些原生组件进行Rx应用。例如:

    //registerButton是一个UIButton,通过.rx.tap能获取到该button的点击事件序列
    _ = registerButton.rx.tap.shareReplay(1).subscribe {(_) in
         //订阅者获取到按钮点击的事件,再做处理
     }.disposed(by: disposeBag)
    
    //nameTextField是一个UITextField控件,可以直接通过.rx.text获取到该控件中输入内容的String序列
    _ = nameTextField.rx.text.shareReplay(1).subscribe {(text) in
         //订阅者实时获取到text内容,再做处理
     }.disposed(by: disposeBag)
    

    上述例子中通过registerButton.rx.tap,我们可以获取该button的点击事件序列,而nameTextField.rx.text给我们提供了UITextField中的文本String的序列,这就像我们通过正常的编码方式获取到的一样:

    //给registerButton添加Target和点击事件
    registerButton.addTarget(self, action: #selector(registerButtonClick), for: .touchUpInside)
    
    @objc func registerButtonClick(){
        //注册按钮点击了
     }
    
    //nameTextField通过通知观察获得UITextField变化的文本
    NotificationCenter.default.addObserver(self, selector: #selector(registerButtonClick), name: NSNotification.Name.UITextFieldTextDidChange, object: nil)
    
    @objc func nameTextFieldValueChanged(){
        //获取到UITextField的text
     }
    

    对比两者不难发现,系统原生的方法有种割裂感,而RxCocoa提供的被观察者以及订阅的模式更符合我们正常思考的模式,在事件发生变化后及时作出回应,而这正是RxSwift响应式编程的核心思想。要达到这种响应式编程的前提条件就是先得有一个可被观察的序列Observable,而RxCocoa中又是怎么将UIButton和UITextField这些继承于UIControl的控件的控制事件转化为一个个不同的Observable事件序列的呢,本篇文章就以UIButton和UITextField为例,探寻RxCocoa源码中的奥秘。

    Observable的创建

    RxSwift中,在Observable的扩展类里提供了一系列创建被观察对象的工厂化方法。例如通过never、empty、just、of等来创建不同的Observable序列,例如:

    Observable.just("🔴")
            .subscribe { event in
                print(event)
            }
            .disposed(by: disposeBag)
    
    Observable.of("🐶", "🐱", "🐭", "🐹")
            .subscribe(onNext: { element in
                print(element)
            })
            .disposed(by: disposeBag)
    

    但如何将一个UIControl的Control Event(包括将来即将发生的点击事件、text变化等等)也转换成对应的序列呢,以上方法好像并不适用,这时就需要用到自定义创建Observable方法了,来看看官方示例:

    let myJust = { (element: String) -> Observable<String> in
            return Observable.create { observer in
                observer.on(.next(element))
                observer.on(.completed)
                return Disposables.create()
            }
        }
            
    myJust("🔴").subscribe { 
            print($0) 
         }.disposed(by: disposeBag)
    

    通过Observable.create方法可以得到一个Observable<E>序列,E是一个泛型类型,在上述例子中E就是String类型

    public static func create(_ subscribe: @escaping (AnyObserver<E>) -> Disposable) -> Observable<E> {
            return AnonymousObservable(subscribe)
        }
    

    而要想达到持续发送事件的目的,只要在@escaping (AnyObserver<E>) -> Disposable) 这个闭包中进行observer.on(.next(element))操作就可以将由element构成的序列发送给订阅者了。
    注意:此处的观察者AnyObserver<E>的观察类型E与你生成的被观察者类型Observable<E>的E要确保一致。而RxCocoa中正是在UIControl扩展中使用了自定义创建Observable.create方法来处理并转化对应的事件。

    UIControl+Rx

    下面分别以UIButton和UITextField为例,来探寻UIControl+Rx这个Reactive限定扩展中的秘密。(关于限定扩展可以看我上一篇文章)
    先来看通过UIButton.rx.tap我们得到了一个什么东西:

    extension Reactive where Base: UIButton {
        
        /// Reactive wrapper for `TouchUpInside` control event.
        public var tap: ControlEvent<Void> {
            return controlEvent(.touchUpInside)
        }
    }
    

    我们得到一个ControlEvent<Void>,而ControlEvent<Void>是什么呢

    //ControlEvent<Void>遵循ControlEventType协议
    public struct ControlEvent<PropertyType> : ControlEventType
    //ControlEventType遵循ObservableType协议
    public protocol ControlEventType : ObservableType 
    

    而我们知道,遵循ObservableType就可以当做一个Observable序列,所以通过对UIButton的.rx.tap我们得到了一个不含任何数据(即Void)的事件序列,再来进一步对源码分析,此处返回的ControlEvent<Void>是通过controlEvent(.touchUpInside)得到的,来看controlEvent方法:

    extension Reactive where Base: UIControl {
        
        /// - parameter controlEvents: Filter for observed event types.
        ///此处传入UIControlEvents控制事件类型,以上UIButton传入的是.touchUpInside点击事件
        public func controlEvent(_ controlEvents: UIControlEvents) -> ControlEvent<Void> {
    
            //通过Observable.create自定义创建一个Observable
            let source: Observable<Void> = Observable.create { [weak control = self.base] observer in
                MainScheduler.ensureExecutingOnScheduler()
    
                guard let control = control else {
                    observer.on(.completed)  // 如果self.base为空,则发送完成事件
                    return Disposables.create()
                }
                //通过控件control和事件controlEvents初始化一个ControlTarget类,
                //在control触发controlEvents事件时,通过闭包回调回来(详见后面)
                let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { control in
                    observer.on(.next())   //一旦有回调,就给订阅者发送事件
                }
            
                return Disposables.create(with: controlTarget.dispose)
            }.takeUntil(deallocated)
            //将自定义序列source包装成ControlEvent返回
            return ControlEvent(events: source)
        }
    

    ControlTarget

    以上源码中最关键是ControlTarget这个初始化方法,通过控件control(UIButton、UITextField等等)和事件controlEvents初始化一个ControlTarget类,在control触发controlEvents事件时,通过闭包回调回来

    final class ControlTarget: RxTarget {
        typealias Callback = (Control) -> Void
        let selector: Selector = #selector(ControlTarget.eventHandler(_:))
        weak var control: Control?
    #if os(iOS) || os(tvOS)
        let controlEvents: UIControlEvents
    #endif
        var callback: Callback?
        #if os(iOS) || os(tvOS)
       //传入控件control(UIButton、UITextField等等)和事件controlEvents
        init(control: Control, controlEvents: UIControlEvents, callback: @escaping Callback) {
            MainScheduler.ensureExecutingOnScheduler()
            self.control = control
            self.controlEvents = controlEvents
            self.callback = callback   //等待self.callback回调
            super.init()
    
            //将controlEvents事件添加到自己,响应selector方法
            control.addTarget(self, action: selector, for: controlEvents)
            let method = self.method(for: selector)
            if method == nil {
                rxFatalError("Can't find method")
            }
        }
    

    该事件触发的方法为#selector(ControlTarget.eventHandler(_:)),eventHandler方法是怎么处理的呢,

    let selector: Selector = #selector(ControlTarget.eventHandler(_:))
    
    //控制事件controlEvents所需要触发的方法
    func eventHandler(_ sender: Control!) {
          if let callback = self.callback, let control = self.control {
              callback(control) //在self.control和callback都不为空时 回调
          }
     }
    

    一目了然,通过上面的callback(control) 我们就能在下面这个回调函数中给订阅者发送事件了

    let controlTarget = ControlTarget(control: control, controlEvents:controlEvents) { control in
         observer.on(.next())   //一旦有回调,就给订阅者发送事件
    }
    

    同理,我们再来看看UITextField.rx.text生成String可观察序列的代码:

    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?> {
             //UIControl.rx.value方法是关键
            return UIControl.rx.value(
                base,
                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
                    }
                }
            )
        } 
    }
    

    接着看UIControl.rx.value这个方法的源码:

    //参数为控件本身,其他分别为两个闭包,
    //其中getter是用来获取UITextField的text值发送给订阅者,setter是设置数值
    static func value<C: UIControl, T>(_ control: C, getter: @escaping (C) -> T, setter: @escaping (C, T) -> Void) -> ControlProperty<T> {
            let source: Observable<T> = Observable.create { [weak weakControl = control] observer in
                    guard let control = weakControl else {
                        observer.on(.completed)
                        return Disposables.create()
                    }
    
                    observer.on(.next(getter(control))) //订阅后首次发送事件
                    //通过ControlTarget来监听UITextField所有跟编辑有关以及值修改的控制事件
                    let controlTarget = ControlTarget(control: control, controlEvents: [.allEditingEvents, .valueChanged]) { _ in
                        if let control = weakControl {
                            observer.on(.next(getter(control))) //一旦有变化就发送给订阅者
                        }
                    }
                    
                    return Disposables.create(with: controlTarget.dispose)
                }
                .takeUntil((control as NSObject).rx.deallocated)
            let bindingObserver = UIBindingObserver(UIElement: control, binding: setter)
            return ControlProperty<T>(values: source, valueSink: bindingObserver)
        }
    

    通过ControlTarget来监听UITextField所有跟编辑有关以及value值发生改变的控制事件,最后生成一个ControlProperty类型的Observable可观察序列,然后发送给订阅者。

    以上就是RxCocoa中如何通过自定义创建Observable,将UIControl控件的控制事件转化为Observable可观察序列的。

    相关文章

      网友评论

        本文标题:RxSwift源码探秘 :如何让UIControl生成可观察序列

        本文链接:https://www.haomeiwen.com/subject/jviuvxtx.html