RxSwift之深入解析dispose源码的实现原理
一、前言
- 任何对象都有生命周期,有创建就要销毁。OC 中有 init 和 dealloc,swift 有 init 和 deinit,RxSwift 也不例外,有 create 和 dispose。
- RxSwift 有两种清除方式:
- 订阅产生的可清除资源(Disposable)对象,调用 dispose 方法清除;
- 通过清除包 DisposeBag 清除,在作用域结束后被释放,也可以在需要的时候置空释放。
- 无论哪种方式,最终都是调用 dispose() 方法来释放。
- 例如,现有如下代码,基础序列的创建和订阅:
// 创建序列let ob = Observable<Any>.create { (observer) -> Disposable inobserver.onNext("ABC")return Disposables.create { print("销毁释放")} // dispose.dispose()}// 序列订阅let dispose = ob.subscribe(onNext: { (anything) inprint("订阅到:\(anything)")}, onError: { (error) inprint("订阅到:\(error)")}, onCompleted: {print("完成")}) {print("销毁回调")}print("执行完毕")// 销毁序列dispose.dispose()
- 运行结果如下所示:
订阅到:ABC执行完毕销毁释放销毁回调
- 可以发现在调用 dispose.dispose() 后,先执行的创建序列的回调闭包 Disposables.create { print(“销毁释放”)},再执行 {print(“销毁回调”)},那么:
- 为什么调用 dispose() 方法能够执行创建序列时的闭包呢?
- 为什么是先执行创建序列的闭包,再执行后面的销毁回调闭包呢?
- dispose() 方法到底是如何实现销毁序列的呢?销毁的是什么?
二、销毁者创建
- 点进 Disposables.create,可以看到返回了一个匿名销毁者 AnonymousDisposable:
public static func create(with dispose: @escaping () -> Void) -> Cancelable {return AnonymousDisposable(disposeAction: dispose)}
fileprivate final class AnonymousDisposable : DisposeBase, Cancelable {public typealias DisposeAction = () -> Void/// Constructs a new disposable with the given action used for disposal.////// - parameter disposeAction: Disposal action which will be run upon calling `dispose`.fileprivate init(_ disposeAction: @escaping DisposeAction) {self._disposeAction = disposeActionsuper.init()}}
- 继续,可以看到在 AnonymousDisposable 里,对象初始化,将外界传入的闭包保存在_disposeAction 里:
fileprivate init(_ disposeAction: @escaping DisposeAction) {self._disposeAction = disposeActionsuper.init()}
- 那么在什么时候调用的呢?继续,可以看到下面的 dispose() 方法:
// 核心逻辑fileprivate func dispose() {if fetchOr(self._isDisposed, 1) == 0 {if let action = self._disposeAction {self._disposeAction = nilaction()}}}
- 这里有个重要的方法,fetchOr(self._isDisposed, 1),它是一个单项标记手段,this.value 初值是 0,所以返回的 oldValue 也是 0。
- 传的 mask 是 1,this.value |= mask 按位或运算,this.value 值变为 1;
- 只有第一次调用 fetchOr,,返回的是 0 , 第二次以后,再调用 fetchOr,返回的都是1。
func fetchOr(_ this: AtomicInt, _ mask: Int32) -> Int32 {this.lock()let oldValue = this.value // 0 1this.value |= mask // 1 1this.unlock()return oldValue // 0 1 1 1 1 1 1 1}
- fetchOr 调用一次后,_isDisposed 就会 变为 1,其实就是属性标记,保证 dispose() 只执行一次。它这个标记方法中,没有直接操作业务属性,所以不会产生依赖,并且使用的是位运算,更加快速。
- dispose() 中,将 _disposeAction 保存到 action,清除 _disposeAction, 执行 action()。销毁的代码只执行一次,所以当前 _disposeAction 置为 nil 后,再执行尾随必包 action:
// 核心逻辑fileprivate func dispose() {if fetchOr(self._isDisposed, 1) == 0 {if let action = self._disposeAction {self._disposeAction = nilaction()}}}
三、销毁 dispose() 方法调用
- 上面的流程,是在 subscriberHandle 回调闭包中,在 subscriberHandle 之还有一个重要的订阅流程 subscribe:
public func subscribe(onNext: ((Element) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)-> Disposable {let disposable: Disposableif let disposed = onDisposed {disposable = Disposables.create(with: disposed)}else {disposable = Disposables.create()}}
- 在 subsricbe 进入订阅方法内容,可以看到:在这里保存外界这个销毁提示的闭包:
if let disposed = onDisposed {disposable = Disposables.create(with: disposed) } else {disposable = Disposables.create()}
- 注意到创建 observer 里的 event,可以看到在 .error 和 .completed 里,都调用 dispose 方法,也就是上面 AnonymousDisposable 里的 dispose 方法,在完成或者报错后,要销毁这个订阅关系:
switch event {case .next(let value):onNext?(value)case .error(let error):if let onError = onError {onError(error)}disposable.dispose()case .completed:onCompleted?()disposable.dispose()}}
- 那么,.next 事件是如何调用 dispose 的呢?我们注意到这里创建了一个销毁者 Disposables,继续进入:
return Disposables.create(self.asObservable().subscribe(observer),disposable)
- 创建一个 BinaryDisposable 二元销毁者,把刚刚的两个要销毁的 disposable 都传进去,返回Disposables可以让外界随时随地的调用 dispose():
// Creates a disposable with the given disposables.public static func create(_ disposable1: Disposable, _ disposable2: Disposable) -> Cancelable {return BinaryDisposable(disposable1, disposable2)}
- 点进 BinaryDisposable,可以看到把传递进来的 disposable1 和 disposable2 都保存起来:
private final class BinaryDisposable : DisposeBase, Cancelable {private let _isDisposed = AtomicInt(0)// stateprivate var _disposable1: Disposable?private var _disposable2: Disposable?/// - returns: Was resource disposed.var isDisposed: Bool {return isFlagSet(self._isDisposed, 1)}/// Constructs new binary disposable from two disposables.////// - parameter disposable1: First disposable/// - parameter disposable2: Second disposableinit(_ disposable1: Disposable, _ disposable2: Disposable) {self._disposable1 = disposable1self._disposable2 = disposable2super.init()}func dispose() {if fetchOr(self._isDisposed, 1) == 0 {self._disposable1?.dispose()self._disposable2?.dispose()self._disposable1 = nilself._disposable2 = nil}}}
- 二元销毁者保存 2 个销毁者对象 _disposable1 和 _disposable2,dispose() 使用 fetchOr 保证销毁代码执行一次,分别调用 2 个销毁者的 dispose() 方法,并设置为 nil。self.asObservable().subscribe(observer) 方法的调用,我们知道订阅流程会来到 Producer 的 subscribe(observer)。这里也看到有一个 dispose() 方法:
func dispose() {if fetchOr(self._isDisposed, 1) == 0 {self._disposable1?.dispose()self._disposable2?.dispose()self._disposable1 = nilself._disposable2 = nil}}
- 那么,self.asObservable().subscribe(observer) 里创建的关键销毁者到底是什么呢?
return Disposables.create(self.asObservable().subscribe(observer),disposable)
- 直接找 Producer 里的 subscribe 方法(为什么直接找 Producer 呢?在 RxSwift 核心逻辑的时候,了解 Producer 里的 subscribe 是会先执行的,具体请参考:RxSwift之深入解析核心逻辑Observable的底层原理),可以看到 SinkDisposer(),如下所示:
let disposer = SinkDisposer()let sinkAndSubscription = self.run(observer, cancel: disposer)
disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)
- 首先看看 self.run 返回的是什么?可以发现,返回的是 AnonymousObservableSink 和 subscription 一个元组类型,subscription 是一个 AnonymousDisposable:
override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {let sink = AnonymousObservableSink(observer: observer, cancel: cancel)let subscription = sink.run(self)return (sink: sink, subscription: subscription)}
- 可能会不理解为什么是 AnonymousDisposable?因为 sink.run 就是调用的这里保存的 _subscribeHandler,而这个 _subscribeHandler 是由外界传递过来的闭包,就是 create 后面跟随的闭包:
// 创建序列let ob = Observable<Any>.create { (observer) -> Disposable inobserver.onNext("1111")return Disposables.create { print("销毁释放了")} // dispose.dispose()}
- 继续,那么 disposer.setSinkAndSubscription 干了什么事情呢?
let disposer = SinkDisposer()let sinkAndSubscription = self.run(observer, cancel: disposer)
disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)
- 这里把 sink 和 subscription 都保存起来了,还可以看到有一个 previousState 的状态,如果状态满足的话,就会调用销毁方法,把这两个都销毁。其实是可以理解为,就是在加入的对象其实需要销毁的,不应该保留的,那么没必要给它继续保留生命周期。
func setSinkAndSubscription(sink: Disposable, subscription: Disposable) {self._sink = sinkself._subscription = subscription// 获取状态 let previousState = fetchOr(self._state, DisposeState.sinkAndSubscriptionSet.rawValue)if (previousState & DisposeState.sinkAndSubscriptionSet.rawValue) != 0 {rxFatalError("Sink and subscription were already set")}// 如果状态满足就销毁if (previousState & DisposeState.disposed.rawValue) != 0 {sink.dispose()subscription.dispose()self._sink = nilself._subscription = nil}}
- 普通的销毁者是 AnonymousDisposable,而这里使用的是 SinkDisposer:
- 初始化 SinkDisposer 类型的 disposer;
- sinkAndSubscription 是子类返回的元组 (sink: sink, subscription: subscription),
sink 保存观察者 observer,销毁者 disposer,subscription 保存的是外界返回的销毁者;
- sinkAndSubscription 是子类返回的元组 (sink: sink, subscription: subscription),
- disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription),disposer 保存 _sink 和 subscription previousState 状态判断对象是否已经销毁,如果已销毁则调用 sink 和 _subscription 的 dispose() 并设置为 nil;
- SinkDisposer 的 func dispose() 通过 previousState 状态保证只销毁一次,
sink 和 _subscription 分别调用 dispose() 并设置为 nil。
- SinkDisposer 的 func dispose() 通过 previousState 状态保证只销毁一次,
- dispose() 方法在什么时候执行?
- 完成和错误信号的响应式必然会直接开启销毁;
- 手动调用 dispose.dispose();
- 系统帮助销毁。
private func dispose() {let oldDisposables = self._dispose()for disposable in oldDisposables {disposable.dispose()}}private func _dispose() -> [Disposable] {self._lock.lock(); defer { self._lock.unlock() }let disposables = self._disposablesself._disposables.removeAll(keepingCapacity: false)self._isDisposed = truereturn disposables}deinit {self.dispose()}
四、销毁的本质
- 通过分析,我们知道 RxSwift 的销毁者实际上销毁的是响应式关系。RxSwift 通过序列和观察者来建立响应关系,如果断开,响应关系就已达到销毁的目标。
- 关于对象的回收,外界观察者和序列会随着它们的作用域空间而释放,内部创建的临时序列和观察者都会随着对外的观察者和序列的生命周期而销毁释放。
五、总结
- Disposables.create(self.asObservable().subscribe(observer),disposable) 调用订阅时创建的 Disposables的dispose(),然后对二元销毁者分别调用 dispose() 并设置为 nil;
- disposable 保存的是订阅时传入的闭包,disposable.dispose() 销毁 RxSwift 与外界的关联。self.asObservable().subscribe(observer) 是 SinkDisposer,因此调用的是 SinkDisposer.dispose();
- SinkDisposer.dispose() 对保存的 2 个属性分别调用 dispose() 并设置为nil,subscription 保存的是外界创建序列时的闭包,因此 subscription.dispose() 也是切断RxSwift 与外界的关联,_sink.dispose() 调用保存的属性_cancel的dispose()。
- RxSwift 为了统一性,会对保存的属性都调用一次 dispose(),如果有相互包含的属性,会有相互调用 dispose() 的情况。比如,SinkDisposer.dispose() 会调用 sink.dispose(),而执行 sink.dispose() 又将会执行 SinkDisposer.dispose()。
RxSwift之深入解析dispose源码的实现原理相关推荐
- 2022最新素材解析网站源码搭建和原理,附带PHP小例子。
一站式素材解析网站源码,资源素材共享网站源码,素材网解析规则分享,素材网站解析下载系统,素材资源解析平台系统源码,素材解析网站制作. 我算是比较早接触素材网站的,因为之前就是做设计的,那时候没那么多套 ...
- 2023最新素材解析网站源码搭建和原理,附带PHP小例子。
我算是比较早接触素材网站的,因为之前就是做设计的,那时候没那么多套路,分享推广就可以获得网站永久VIP,然后,现在变得吃相极其难看了,各类型的VIP区分,想让客户二次付费.由此就诞生了很多代下的服务, ...
- Java定时任务(一) Timer及TimerTask的案例解析及源码分析
Java定时任务(一) Timer及TimerTask的案例解析及源码分析 一.概述: 定时任务这个概念在Java的学习以及项目的开发中并不陌生,应用场景也是多种多样.比如我们会注意到12306网站 ...
- php项目素材,PHP素材资源解析平台源码V8.0(thinkPHP框架内核)
PHP素材资源解析平台源码V8.0 第三方平台下载千图网千库网等素材网站下载站 V8版本.十月一日最 新更新,全新的解析架构. 小白问题一:为什么我不能解析? 答:解析是需要开通目标站VIP的. 小白 ...
- Jsoup 解析Html源码实例
最近在做数据挖掘的过程中需要对html的源码进行解析,用到了Jsoup这个解析工具,下面写个基本实例来展现它的用法. 需要用到jar包:jsoup-1.7.2.jar,可以到jsoup的官网下载:ht ...
- php 记事本源代码_抖音无水印解析PHP源码
抖音无水印解析PHP源码 仅限学习使用 输出为标准的格式化json数据 ajax直接调用即可 原生的ajax的调用方法看底部 $url = @$_GET['url']; if (strstr($url ...
- 最新抖音无水印解析PHP源码
源码介绍: 最新抖音无水印解析PHP源码 网盘下载地址: http://www.bytepan.com/hQuJxMu7aaG 图片:
- 【原创】微云网盘直链解析C#源码
[原创]微云网盘直链解析C#源码 不废话,直接上代码,firebug分析的. usingSystem;usingSystem.Collections.Generic;usingSystem.Text; ...
- 全民K歌PHP解析源码下载,全民K歌解析html源码
效果图: 全民K歌解析html源码 新建一个html文件,复制下方代码粘贴保存上传即可 使用:复制全民K歌作者主页地址粘贴即可解析全部歌曲html> 全民K歌获取用户歌曲,全民K歌api,全民K ...
最新文章
- 【数据结构复习】(1)绪论
- 【struts2+hibernate+spring项目实战】java监听器实现权限控制系统和资源获取优化(ssh)
- CADisplayLink的简单使用
- CSS系列讲解-总目录
- 服务器创建多个dhcp服务_如何在15分钟内创建无服务器服务
- Spring入门学习手册 2:怎么用注解来DI/IOC
- 【Python3网络爬虫开发实战】1.5.2-PyMongo的安装
- java中什么是底层数据结构_JavaScript 对象的底层数据结构是什么
- 计算机方向关键字,从计算机的角度理解volatile关键字
- 不能说服别人接受,只能是个烂设计
- unity 预编译指令
- SAP案例教程FI财务后台配置
- 【MMCV 源码解读】一、Config(配置文件相关)
- 智能纪元,简述NVIDIA的伟大航路
- 为tableview添加带控件的单元格如复选框checkbox与combbox单元格
- 【MATLAB】在MATLAB中利用GUI编写加法计算器,要求:通过两个编辑文本框实现两个数字的输入,点击“开始计算”按钮进行计算,并在用于结果显示的静态文本框中实现两输入数字的和的显示
- 硬盘数据迁移软件哪款好用?强烈推荐它!
- [Python] js逆向初探: 某麦榜单
- 初学Epicor二次开发地址
- webclient java_Spring5-WebClient之java探针开发总结复盘