RxSwift核心之Disposable

1,492 阅读7分钟

一般来说,一个可观察序列发出了 error 或者 completed 事件,那么所有内部资源都会被释放。但是如果你需要提前释放这些资源或者取消订阅的话,那么你可以对返回的 Disposable(可被清除的资源) 调用 dispose 方法。 调用 dispose 方法后,订阅将被取消,并且内部资源都会被释放掉。

举个栗子:

// 第一步:创建序列
let ob = Observable<String>.create { (observer) -> Disposable in
    
    // 第三步:发送信号
    observer.onNext("信息1")
    
    return Disposables.create{ print("销毁释放了") }
}

// 第二步:订阅信号
let disposable = ob.subscribe(onNext: { (text) in
    print("订阅信息: \(text)")
}, onError: { (error) in
    print("error: \(error)")
}, onCompleted: {
    print("订阅结束")
}) {
    print("已销毁")
}

// 取消订阅/释放
disposable.dispose()
执行结果:
订阅信息: 信息1
销毁释放了
已销毁

基于以上示例,来分析一下底层逻辑。

subscribe

我们再查看一下 subscribe 源码。

在之前的RxSwift核心逻辑简介中,我们仅分析了 self.asObservable().subscribe(observer)这个订阅逻辑,因为订阅流程基本是一个独立的完成逻辑,而 dispose 销毁逻辑也是一个相对独立的逻辑,所以可以分开来分析。那我们现在就来看看 Disposables.create 源码。
Disposables.create 创建了一个 BinaryDisposable 对象的实例。 BinaryDisposable 初始化时,需要两个 Disposable 参数。我们分别来分析这两个参数具体是什么,因为参数2相对简单一些,那我们先从简单的开始分析。

  • 参数2disposable: 通过 subscribe 源码可以知道,因为示例中 onDisposed 闭包有代码块 print("已销毁") 所以 disposable = Disposables.create(with: disposed)

    通过分析源码,可以清楚的知道,disposable 是一个保存有销毁闭包的 AnonymousDisposable 匿名销毁者。

  • 参数1self.asObservable().subscribe(observer): 在RxSwift核心逻辑简介中已经分析了,这句代码最终会调用 Producer.subscribe

    所以最后会返回一个 SinkDisposer 的实例对象。
    SinkDisposer 也需要两个参数,而这两个参数均来自于 let sinkAndSubscription = self.run(observer, cancel: disposer) 。那我们继续深入 run 函数。
    run 函数返回一个元组,元组第一个值是 sink,第二个是 sink.run(self) 返回的结果,根据RxSwift核心逻辑简介的分析可以知道,sink.run(self) 返回的结果即为创建序列的闭包返回值 Disposables.create{ print("销毁释放了") }


至此,两个参数的分析结束。那我们再来分析 disposable.dispose() 这个流程到底做了些什么?

在分析流程之前,先插入一个小的知识点。在刚才的源码中,细心的小伙伴可以会发现,很多地方都调用了 fetchOr 函数,那么,这个函数具体是什么作用呢?我们先来分析一下。

func fetchOr(_ this: AtomicInt, _ mask: Int32) -> Int32 {
    this.lock()
    let oldValue = this.value
    this.value |= mask
    this.unlock()
    return oldValue
}

这就是 fetchOr函数源码。 lock() 只是加锁,可以不管,那么 fetchOr 函数可以简化为:

func fetchOr(_ this: AtomicInt, _ mask: Int32) -> Int32 {
    let oldValue = this.value
    this.value |= mask
    return oldValue
}

this 是传入的AtomicInt值,其内部仅有一个value值。 fetchOr 先将 this.value copy一份,作为结果返回。而将 this.valuemask 做或 (|) 运算。并且将或运算的结果赋值给 this.value。 可能这样描述不是很清晰,那我们用图表的方式来直观展示。

fetchOr 函数的作用类似于标记。具体的使用,后面再结合使用场景分析。

disposable.dispose()

经过上面的分析可以知道,示例中执行 disposable.dispose() 会调用 BinaryDisposable 中的 dispose() 函数。

func dispose() {
    if fetchOr(self._isDisposed, 1) == 0 {
        self._disposable1?.dispose()
        self._disposable2?.dispose()
        self._disposable1 = nil
        self._disposable2 = nil
    }
}

这里有一个 fetchOr 函数。self._isDisposed 值为 AtomicInt(0) 则,this.value = 0mask = 1,那么,最后的返回值为0,且 self._isDisposed 中的 value 变为 1。即这里的 fetchOr 函数仅在第一次执行时返回值为 0,以后均为 1也意味着,dispose() 只会执行一次。

dispose() 对两个初始化时传入的参数都调用 dispose()。调用完成后,都置为nil,完成订阅的销毁。那么,我们按照调用顺序,再深入理解其中的销毁逻辑。

  1. self._disposable1?.dispose() 在对 _disposable1 调用 dispose() 时,其实是调用 SinkDisposerdispose 函数。

    最终就会调用 sink.dispose()subscription.dispose()以及将 self._sinkself._subscription置为nil。

    • sink.dispose() sink.dispose() 就会调用 AnonymousObservableSinkdispose()。 根据源码分析,dispose() 会执行 AnonymousObservableSink 初始化时传入的 canceldispose(),而 cancel 又是 Producer.subscribe 中初始化的 SinkDisposer 实例对象。即会回到SinkDisposerdispose 函数,但是因为 fetchOr 函数的存在,dispose 函数仅会执行一次。所以 sink.dispose() 相当于空转一圈,什么也没有执行。

    • subscription.dispose() 在上一节的分析中,已经知道 subscription 其实保存的是创建序列的闭包返回值 Disposables.create{ print("销毁释放了") },是一个匿名销毁者 AnonymousDisposable。 即 AnonymousDisposable 调用 dispose()

      最终会执行保存的闭包,打印出 销毁释放了。并将保存的闭包置为nil。

    • self._subscription = nil

      释放保存的闭包。

    • self._sink = nil

      self._sink 置为nil,是整个销毁流程中的关键。通过之前[RxSwift核心逻辑简介]的分析,已经明确了 sink 的重要性,其担当起了连接 可观察序列观察者调度者的重要作用,现在又关联了销毁者,可以说,没有了 sink,那么所有的流程都无法正常流通了,所以将 self._sink置为nil,则打断了可观察序列观察者之间的联系,以及释放了所有中间临时对象。

    完成 self._disposable1?.dispose() 的调用。

  2. self._disposable2?.dispose() 在上一节也分析了,_disposable2 是保存有订阅时 subscribe 函数的 onDisposed闭包参数的 AnonymousDisposable。其调用 dispose 函数时,也会回调其中保存的闭包 print("已销毁"),所以会打印出 已销毁

至此,Disposable 可被清除的资源 的底层分析已经完成。

总结:经过上面的销毁者源码分析,可以知道,销毁者(Disposable)的核心流程主要有3个:

  1. 销毁连接 可观察序列观察者AnonymousObservableSink 对象,打断其中连接,取消订阅。--这也是其核心逻辑
  2. 回调创建序列时返回的Disposables.create{ print("销毁释放了") } 中的闭包。
  3. 回调订阅信号时的 onDisposed 闭包。

Disposable的实际应用 DisposeBag

在通常情况下,我们是不需要手动调用 dispose 函数的,上面的示例只是为了便于分析其底层逻辑。在实际使用中,我们一般使用 DisposeBag 来自动管理订阅的生命周期。

示例:

let subject = PublishSubject<String>()

subject.onNext("🐘")

subject.subscribe(onNext: { print("订阅到了: \($0)") })
    .disposed(by: disposeBag)
其中:disposeBag 是Controller持有的属性。
当Controller被销毁时,所有的订阅都会被销毁。

下面我们来分析一下 DisposeBag 的底层实现。

  • dipsosed()

    dipsosed() 是调用了 DisposeBag.insert 方法,将销毁者添加到 bag 中。

    查看 DisposeBag 的源码。

    insert 函数中执行了 _insert 函数,并对其返回值调用 dispose 方法。 因为 self._isDisposed值为false,_insert 函数返回 nil。同时将销毁者添加到 self._disposables 数组中保存起来。

  • 自动销毁时机 使用 DisposeBag 后,不再需要我们手动管理订阅销毁,那么肯定是 DisposeBag 帮我们自动处理了销毁工作。那么它是在什么时候执行销毁的呢? DisposeBag自动 管理销毁,那么只能在其生命周期函数中才能实现 自动 执行。我们继续查看其源码。

    我们可以在 DisposeBag 的源码中,清晰的看到 deinit 方法。 其调用了自身的 dispose 方法。

    dispose 函数中通过 self._dispose() 获取到所有保存的销毁者,并对每个销毁者执行 dispose 销毁操作。 _dispose 函数copy一份销毁者数组并返回。而移除 self._disposables 中保存的所有元素。这样做这要是为了隔离源数据,在对销毁者执行销毁操作时,不会因外界的环境变化而对内部的销毁产生影响。

以上就是 DisposeBag 的源码分析,其核心原理是在生命周期结束执行 deinit 方法时,对所有内部保存的销毁者调用 dispose 方法,完成销毁订阅的操作。

以上RxSwift核心之Disposable的底层分析,若有不足之处,请评论指正。