RxSwift-UITextField事件监听源码分析

UITextField是常用的控件。RxSwiftUITextField的事件.valueChanged, .editingDidBegin, .editingChanged, .editingDidEnd, .editingDidEndOnExit进行封装。
使用方法如下:

  textFiled.rx.text
            .subscribe(onNext: { (text) in
                print("打印: \(text)") // block_1
        })

运行代码:会默认执行一次block_1闭包。
如果textFiled.text = "Jensen"代码,并不会执行block_1闭包。它是怎么实现的呢?
带着疑问,我们进入源码。

textFiled.rx.text做了什么?

public var text: ControlProperty<String?> {
        return value
    }

返回的是一个ControlProperty对象,下图是它的继承关系:

ControlProperty.png

通过她的继承关系,我们知道ControlProperty是个可观察序列,也可以作为观察者。

代码中返回了value:

  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
                }
            }
        )
    }

调用base.rx.controlPropertyWithDefaultEvents方法,参数是UITextField.textgettersetter方法

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
        )
    }

调用controlProperty方法初始化:

public func controlProperty<T>(
        editingEvents: UIControl.Event,
        getter: @escaping (Base) -> T,
        setter: @escaping (Base, T) -> Void
    ) -> ControlProperty<T> {
        let source: Observable<T> = Observable.create { [weak weakControl = base] observer in
                guard let control = weakControl else {
                    observer.on(.completed)
                    return Disposables.create()
                }

                observer.on(.next(getter(control)))

                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)

        let bindingObserver = Binder(base, binding: setter)

        return ControlProperty<T>(values: source, valueSink: bindingObserver)
    }

controlProperty首先创建一个可观察序列source(本文将其命名为source_1,本文中再次出现source_1指的就是这个地方创建的这个可观察序列)。创建序列的逻辑实现可查看RxSwift核心逻辑分析,本文不在赘述。
let bindingObserver = Binder(base, binding: setter)创建一个Binder观察者.

public init<Target: AnyObject>(_ target: Target, scheduler: ImmediateSchedulerType = MainScheduler(), binding: @escaping (Target, Value) -> Void) {
        weak var weakTarget = target

        self._binding = { event in
            switch event {
            case .next(let element):
                _ = scheduler.schedule(element) { element in
                    if let target = weakTarget {
                        binding(target, element)
                    }
                    return Disposables.create()
                }
            case .error(let error):
                bindingError(error)
            case .completed:
                break
            }
        }
    }

Binder观察者中保存了target_binding闭包。

·ControlProperty初始化方法保存了_values_valueSink

 public init<Values: ObservableType, Sink: ObserverType>(values: Values, valueSink: Sink) where Element == Values.Element, Element == Sink.Element {
        self._values = values.subscribeOn(ConcurrentMainScheduler.instance)
        self._valueSink = valueSink.asObserver()
    }

_valueSink就是上述创建的观察者let bindingObserver = Binder(base, binding: setter)
_values保存的是subscribeOn对象,

    public func subscribeOn(_ scheduler: ImmediateSchedulerType)
        -> Observable<Element> {
        return SubscribeOn(source: self, scheduler: scheduler)
    }

final private class SubscribeOn<Ob: ObservableType>: Producer<Ob.Element> {
    let source: Ob
    let scheduler: ImmediateSchedulerType
    
    init(source: Ob, scheduler: ImmediateSchedulerType) {
        self.source = source
        self.scheduler = scheduler
    }
}

SubscribeOn保存了values,即是上述创建的source_1

上面就是textFiled.rx.text做的事情,就是创建了一个中间层ControlProperty,保存了一个可观察序列source_1,和它的观察者对象_valueSink

WechatIMG72.jpeg

订阅方法subscribe

结合RxSwift核心逻辑分析,ControlProperty重写了subscribe(observer)方法,序列的订阅会调用到ControlPropertysubscribe(observer)

 public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
        return self._values.subscribe(observer)
    }

self._valuesSubscribeOn对象,那么self._values.subscribe等价于SubscribeOn.subscribe
根据RxSwift核心逻辑分析,会调用到SubscribeOn.run方法,然后调用sink.run()

func run() -> Disposable {
        let disposeEverything = SerialDisposable()
        let cancelSchedule = SingleAssignmentDisposable()
        
        disposeEverything.disposable = cancelSchedule
        
        let disposeSchedule = self.parent.scheduler.schedule(()) { _ -> Disposable in
            let subscription = self.parent.source.subscribe(self)
            disposeEverything.disposable = ScheduledDisposable(scheduler: self.parent.scheduler, disposable: subscription)
            return Disposables.create()
        }

        cancelSchedule.setDisposable(disposeSchedule)
    
        return disposeEverything
    }

self.parent.source.subscribe(self),self.parentSubscribeOn,SubscribeOn.source正是上面创建的source_1, 根据RxSwift核心逻辑分析,调用可观察序列的subscribe,会执行可观察序列创建时传入的逃逸闭包,

let source: Observable<T> = Observable.create { [weak weakControl = base] observer in
                guard let control = weakControl else {
                    observer.on(.completed)
                    return Disposables.create()
                }

                observer.on(.next(getter(control)))

                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)

闭包中,首先默认执行observer.on(.next(getter(control))),这也就是为什么第一次订阅会默认调用一次block_1.
然后是ControlTarget的创建

init(control: Control, controlEvents: UIControl.Event, callback: @escaping Callback) {
        MainScheduler.ensureRunningOnMainThread()

        self.control = control
        self.controlEvents = controlEvents
        self.callback = callback

        super.init()

        control.addTarget(self, action: selector, for: controlEvents)

        let method = self.method(for: selector)
        if method == nil {
            rxFatalError("Can't find method")
        }
    }

ControlTargetUITextField添加事件,当有时间触发时,就会调用callback,执行外界的block_1。这也就是为什么上述直接对控件赋值,无法执行block_1闭包的原因了,因为直接对控件赋值不属于事件。

总结:

上述是UITextField事件封装及调用的全过程。
简要的说,UITextField事件的封装其实是创建了中间层ControlProperty,ControlProperty保存一个内部可观察序列source_1,及其它的观察者。当调用ControlProperty.subscribe进行订阅时,内部将会转为对内部序列source_1的订阅,对source_1的订阅会执行source_1创建时的尾随闭包,闭包中首先调用一次observer.on,并添加UITextField的事件监听。

t.png

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349