RxSwift原理2 - 按钮事件订阅

序章

在上一篇文章中,已经介绍了最基础的订阅与发布,那么按钮事件的订阅又是如何实现的呢,先看一下demo代码

let _ = addButton.rx.tap.subscribe { [weak self] in
    print("+号 next")
    self?.count += 1
    self?.countString.accept(String(self?.count ?? 0))
} onError: { _ in
    
} onCompleted: {
    print("+号 completed")
} onDisposed: {
    print("+号 订阅释放")
}

好了,我们现在来点击addButton看看会输出什么:

+号 next

正确执行了OnNext的闭包方法。细心的你可能会发现onCompleted、onDisposed并没有执行,这个后续写Disposed会详细说明,本文暂不涉及。

第一讲 - rx与tap

在深入了解他的实现之前,我们先需要认识一下rx与tap

走进rx

extension ReactiveCompatible {
    public static var rx: Reactive<Self>.Type {
        get { Reactive<Self>.self }
        set { }
    }

    public var rx: Reactive<Self> {
        get { Reactive(self) }
        set { }
    }
}

extension NSObject: ReactiveCompatible { }

看源码我们可以看到,rx是ReactiveCompatible协议中的方法,而NSObject遵循了这个协议,使得button可以调用rx方法。

而rx方法的返回的是一个Reactive对象,参数self即为button本身

public struct Reactive<Base> {
    public let base: Base
    public init(_ base: Base) {
        self.base = base
    }
}

Reactive就是一个结构体,而在本文中,button即被保存在base中

走进tap

tap方法的实现其实还是Reactive,看源码

extension Reactive where Base: UIButton {
    public var tap: ControlEvent<Void> {
        controlEvent(.touchUpInside)
    }
}

extension Reactive where Base: UIControl {
    public func controlEvent(_ controlEvents: UIControl.Event) -> ControlEvent<()> {
        //...省略
        return ControlEvent(events: source)
    }
}

可以看到tap方法实际调用的就是controlEvent方法, 并返回一个ControlEvent对象(ControlEvent遵循ObservableType协议)。
所以序章中的代码可以转换为以下代码:

//创建序列
let ob =  Reactive(addButton).controlEvent(.upInside) {
    //...省略
    return ControlEvent(events: source)
}
//订阅消息
let _ = ob.subscribe { [weak self] in
    print("+号 next")
    self?.count += 1
    self?.countString.accept(String(self?.count ?? 0))
} onError: { _ in
    
} onCompleted: {
    print("+号 completed")
} onDisposed: {
    print("+号 订阅释放")
}

第二讲 - 事件转移

你可能好奇为什么标题是事件转移,直接说结果,其实就是RxSwift截胡了addButton的点击事件并在内部实现,进而转换为OnNext事件。接下来我们一步一步看他的具体实现:

  1. 调用controlEvent方法,创建了source,也就是原理1种所说的创建序列,此时其尾随闭包还未调用,返回controlEvent对象
public func controlEvent(_ controlEvents: UIControl.Event) -> ControlEvent<()> {
    let source: Observable<Void> = Observable.create { [weak control = self.base] observer in
            let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { _ in
                observer.on(.next(()))
            }
            return Disposables.create(with: controlTarget.dispose)
        }
        .take(until: deallocated)
    return ControlEvent(events: source)
}
  1. 执行tap.subscribe方法,也就是原理1所说的订阅消息,创建订阅者和订阅闭包
public func subscribe(
        onNext: ((Element) -> Void)? = nil,
        onError: ((Swift.Error) -> Void)? = nil,
        onCompleted: (() -> Void)? = nil,
        onDisposed: (() -> Void)? = nil
    ) -> Disposable {
            let disposable: Disposable
            //...省略代码
            let observer = AnonymousObserver<Element> { event in
                switch event {
                case .next(let value):
                    onNext?(value)
                //...省略代码
            }
            return Disposables.create(
                self.asObservable().subscribe(observer),
                disposable
            )
    }
  1. 紧接着执行self.asObservable().subscribe(observer)方法, 而此时的self即时是一个controlEvent对象,也就是执行controlEvent中的asObservable和subscribe方法
public struct ControlEvent<PropertyType> : ControlEventType {
    public typealias Element = PropertyType
    let events: Observable<PropertyType>
    //将第一步中的source与ConcurrentMainScheduler.instance关联,返回并保存关联后对象
    public init<Ev: ObservableType>(events: Ev) where Ev.Element == Element {
        self.events = events.subscribe(on: ConcurrentMainScheduler.instance)
    }
    public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
        self.events.subscribe(observer)
    }
    public func asObservable() -> Observable<Element> {
        self.events
    }
}
  1. 也就是执行Producer中的subscirbe方法,即创建sink管道,保存第2步中的订阅者。
  2. 执行sink.run(self)方法,此处self即第1步创建的controlEvent对象,进而执行第1步中的尾随闭包
  3. 在ControlTarget中,实现了点击方法,并调用了callback,也就是触发了序章中的onNext方法
final class ControlTarget: RxTarget {
    typealias Callback = (Control) -> Void
    let selector: Selector = #selector(ControlTarget.eventHandler(_:))
    weak var control: Control?
    var callback: Callback?
    init(control: Control, controlEvents: UIControl.Event, callback: @escaping Callback) {
        MainScheduler.ensureRunningOnMainThread()
        self.control = control
        self.controlEvents = controlEvents
        self.callback = callback
        super.init()
        //内部实现了addTarget方法
        control.addTarget(self, action: selector, for: controlEvents)
        let method = self.method(for: selector)
        if method == nil {
            rxFatalError("Can't find method")
        }
    }
    @objc func eventHandler(_ sender: Control!) {
        //回调中触发callBack方法
        if let callback = self.callback, let control = self.control {
            callback(control)
        }
    }
}

到这里,整个逻辑就梳理完成了。

总结

最后画个图总结一下吧


yuque_mind (1).jpeg
  • 先按照黑色箭头执行
  • sink.run回到controlEvent,按照绿色箭头执行
  • 最后用户点击事件触发红色箭头
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容