RxSwift: 麻煩給我的愛人來一個 DisposeBag
序
上篇文章 mojito: 麻煩給我的愛人來一份 RxSwift
沒有提及 DisposeBag
回收機制
原因有其二
一是因為
時間太短,我沒來及看
二是因為
文章過長,必口腔潰瘍
Disposable
話不多說,來的都是回頭客
看一下經典案例
let obable = Observable<String>.create { observer -> Disposable in
observer.onNext("dispose") # step1
observer.onCompleted() # step2
return Disposables.create {
print ("Disposables 釋放") # step6
}
}
_ = obable.subscribe(onNext: { (str) in
print("序列" + str) # step3
},onError: { _ in
},onCompleted: {
print("完成回撥") # step4
},onDisposed: {
print("銷燬回撥") # step5
})
// 列印順序 序列dispose -> 完成回撥 -> 銷燬回撥 -> Disposables 釋放
複製程式碼
Q 1:
為什麼訂閱建立時 閉包返回值是 Disposable
先理清一些概念
Disposable
# 名為 Disposable 的協議,定義一個重要的方法 dispose
public protocol Disposable {
# 處理 回收資源.
func dispose()
}
複製程式碼
Disposables
# Disposables 是 結構體
public struct Disposables {
private init() {}
}
# Disposables 擴充套件 create,返回值 AnonymousDisposable 物件
# 銷燬者1
extension Disposables {
public static func create(with dispose: @escaping () -> Void) -> Cancelable {
return AnonymousDisposable(disposeAction: dispose)
}
}
複製程式碼
Disposables.create
,建立了 匿名銷燬者 AnonymousDisposable
物件, 將閉包 step6
傳入
返回值是 Cancelable
型別
# 名為 Cancelable 的協議,繼承 Disposable
public protocol Cancelable : Disposable {
# 資源是否被銷燬過.
var isDisposed: Bool { get }
}
複製程式碼
再看一下 AnonymousDisposable
# 繼承了 DisposeBase 和 Cancelable,同時擁有 isDisposed & dispose()
private final class AnonymousDisposable : DisposeBase,Cancelable {
# Disposables.create 建立的閉包
public typealias DisposeAction = () -> Void
private let _isDisposed = AtomicInt(0)
private var _disposeAction: DisposeAction?
# 是否被銷燬
public var isDisposed: Bool {
return isFlagSet(self._isDisposed,1)
}
# 儲存閉包
private init(_ disposeAction: @escaping DisposeAction) {
self._disposeAction = disposeAction
super.init()
}
# 核心方法 私有僅供自己使用
fileprivate func dispose() {
if fetchOr(self._isDisposed,1) == 0 {
if let action = self._disposeAction {
self._disposeAction = nil
action()
}
}
}
}
複製程式碼
AtomicInt(0)
: 繼承 NSLock
, 賦值原始值為 0
fetchOr
和 isFlagSet
都是 AtomicInt 類 的方法,都是執行緒安全
的
isFlagSet
:
func isFlagSet(_ this: AtomicInt,_ mask: Int32) -> Bool {
return (load(this) & mask) != 0
}
# 當 `self._isDisposed` 不為0 的時候,也就是 被銷燬過
# 則 isFlagSet 返回 true,即 isDisposed 為true
複製程式碼
fetchOr
:
func fetchOr(_ this: AtomicInt,_ mask: Int32) -> Int32 {
this.lock()
let oldValue = this.value
this.value |= mask
this.unlock()
return oldValue
}
# 進行 位運算 | ,只有當第一次為 0 的時候,返回 oldValue 0
# 外界等式 成立
# newValue 為 位運算之後的值 1,下次等式 就不等於 0
# 目的:只銷毀一次
複製程式碼
dispose()
if let action = self._disposeAction {
self._disposeAction = nil
action()
}
# 臨時變數 action 儲存 外界閉包
# 外界閉包 置nil
# 執行閉包
複製程式碼
到這裡,我們小結一下
- 小結
-
Disposables.create
是_subscribeHandler
閉包執行回撥時,開始執行的 - 當 執行
_subscribeHandler
,勢必產生一個匿名銷燬者AnonymousDisposable
-
AnonymousDisposable
例項,持有外界銷燬閉包
-
AnonymousDisposable
,具有銷燬的能力且只會銷燬一次
,以及是否銷燬
的判斷
-
銷燬者繼承鏈如下:
Disposables
看到這裡就產生了第二個問題
Q 2:
dispose
什麼時候呼叫 ?
時間回溯到 上篇的 subscribe
public func subscribe(onNext: ((Element) -> Void)? = nil,onError: ((Swift.Error) -> Void)? = nil,onCompleted: (() -> Void)? = nil,onDisposed: (() -> Void)? = nil)
-> Disposable {
let disposable: Disposable
# 將外界傳入的 onDisposed 閉包 , 即 print("銷燬回撥")
# 生成 匿名銷燬者 AnonymousDisposable 例項
if let disposed = onDisposed {
disposable = Disposables.create(with: disposed)
} else {
disposable = Disposables.create()
}
let observer = AnonymousObserver<Element> { event in
switch event {
...省略
case .error(let error):
disposable.dispose()
case .completed:
disposable.dispose()
}
}
# 又 return 一個 Disposables.create
return Disposables.create(
self.asObservable().subscribe(observer),disposable # 銷燬者2 onDisposed
)
}
複製程式碼
不難看出,外界呼叫 subscribe
的時候,將 onDisposed 閉包傳入
而 subscribe
的返回值 也是 Disposable
型別,也是通過 Disposables.create
返回
定睛一看
這個 Disposables.create
, 有2個
引數
奇怪的事情發生了
點進去
extension Disposables {
# 通過2個銷燬者 ,生成一個新的銷燬者
public static func create(_ disposable1: Disposable,_ disposable2: Disposable) -> Cancelable {
return BinaryDisposable(disposable1,disposable2)
}
}
複製程式碼
不是看在一個爹的份上,你祖墳今天可能沒了
Disposables.creat 銷燬者1 說到
BinaryDisposable
銷燬者,和之前分析的 AnonymousDisposable
如出一轍,這裡就不分析了
private final class BinaryDisposable : DisposeBase,Cancelable {
....
# 只是內部多了一個引數
var isDisposed: Bool {
return isFlagSet(self._isDisposed,1)
}
func dispose() {
if fetchOr(self._isDisposed,1) == 0 {
self._disposable1?.dispose()
self._disposable2?.dispose()
self._disposable1 = nil
self._disposable2 = nil
}
}
}
複製程式碼
問題又來到了這個 二元銷燬者
的第一個引數 self.asObservable().subscribe(observer)
可以在 Producer
中 找到它的身影
override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
# 管道清潔工
# 返回一個 disposer 例項,在它釋放時,釋放管道內 所有引用
let disposer = SinkDisposer()
# 把 disposer 和 訂閱者 傳入 序列AnonymousObservable 的 run 中
let sinkAndSubscription = self.run(observer,cancel: disposer)
disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink,subscription: sinkAndSubscription.subscription)
return disposer
}
複製程式碼
建立了一個 管道清潔工 SinkDisposer
例項 disposer ,通過呼叫 AnonymousObservable 的 run
,將生成的 sink
和 subscription
分別持有
並返回 disposer
AnonymousObservable
中
final private class AnonymousObservable<Element>: Producer<Element> {
override func run<Observer: ObserverType>(_ observer: Observer,cancel: Cancelable) -> (sink: Disposable,subscription: Disposable) where Observer.Element == Element {
# 通過上面傳入的 銷燬者,和訂閱者 生成匿名觀察管道
let sink = AnonymousObservableSink(observer: observer,cancel: cancel)
# 呼叫run ,也就是 執行 _subscribeHandler 閉包 ,將返回值 賦值給subscription
# subscription 指的是 銷燬者1
let subscription = sink.run(self)
return (sink: sink,subscription: subscription)
}
}
複製程式碼
AnonymousObservableSink 繼承於 Sink
,Sink 同樣繼承於 Disposable
,代表了 AnonymousObservableSink
同樣具有 dispose()
的能力
老師,我暈了
能不能把 DisposeBag
先給我
我吐一下
Q 3
: AnonymousObservableSink
和 SinkDisposer
之間的關係是什麼?
A 3:
SinkDisposer 將自己的例項,通過匿名觀察序列,傳入 管道 AnonymousObservableSink
中,管道 Sink
擁有最終解釋權
意味著當外界執行訂閱subscribe
的時候, 會creat 一個 Disposables
元祖
Disposables
元祖內包含 :
- onDisposed 銷燬者2
- 管道清潔工 SinkDisposer
- sink
- AnonymousObservableSink
- subscription
- 銷燬者1
- sink
因為加入了 Sink
的概念,Sink 作為轉換者,處理了 序列 和訂閱者 以及 銷燬者之間的聯絡
如果把 Sink
比作 一棟大廈
那 SinkDisposer
就是大廈內的清潔工 ,SinkDisposer
負責處理大廈一切 雜物,但排程能力及解釋權
還是歸大廈 Sink
所屬
SinkDisposer
private final class SinkDisposer: Cancelable {
private enum DisposeState: Int32 {
case disposed = 1
case sinkAndSubscriptionSet = 2
}
# 初始值為 0
private let _state = AtomicInt(0)
private var _sink: Disposable?
private var _subscription: Disposable?
# 外界先設定 setSinkAndSubscription
func setSinkAndSubscription(sink: Disposable,subscription: Disposable) {
self._sink = sink
self._subscription = subscription
# 0 | 2,第一次返回oldValue 0,previousState 為 0
# _state 賦值為 2
let previousState = fetchOr(self._state,DisposeState.sinkAndSubscriptionSet.rawValue)
# 第一次 previousState 為0, 0 & 2 = 0,繼續走
if (previousState & DisposeState.sinkAndSubscriptionSet.rawValue) != 0 {
rxFatalError("Sink and subscription were already set")
}
# 0 & 1 = 0,不進 if
if (previousState & DisposeState.disposed.rawValue) != 0 {
sink.dispose()
subscription.dispose()
self._sink = nil
self._subscription = nil
}
}
# 銷燬時呼叫,上面方法已走過一次
func dispose() {
# 在初次呼叫 setSinkAndSubscription 後, _state 為2, 2 | 1 = 3, fetchOr 返回舊值 2 ,_state 變為 3
# previousState 為 2
let previousState = fetchOr(self._state,DisposeState.disposed.rawValue)
# 2 & 1 = 0,不進 if, 繼續走
if (previousState & DisposeState.disposed.rawValue) != 0 {
return
}
# 2 & 2 = 2, 滿足條件,進 if ,分別 dispose
if (previousState & DisposeState.sinkAndSubscriptionSet.rawValue) != 0 {
# AnonymousObservableSink 呼叫 dispose
sink.dispose()
# 銷燬者1 呼叫 dispose
subscription.dispose()
# 銷燬後,分別置nil
self._sink = nil
self._subscription = nil
}
}
}
複製程式碼
Q A
Q 1:
為什麼訂閱建立時 閉包返回值是 Disposable 型別 ?
Disposable 型別 指的是 匿名銷燬者,
即銷燬者1
。因為想在序列建立的時候,就給它挖好墳墓,立好墓碑
如 燼
所說
我於殺戮之中盛放,亦如黎明中的花朵
死不重要,帥就完事了
Q 2:
dispose
什麼時候呼叫 ?
在本文中,顯式呼叫了 onCompleted
,即 執行 subscribe
的 onCompleted閉包
,並且接著 呼叫
disposable.dispose()
case .completed:
onCompleted?()
disposable.dispose() # 銷燬者2
}
複製程式碼
即 列印順序為 完成回撥 -> 銷燬回撥
而 此時的 發出onCompleted
訊號,必然會順著 水管 流到 AnonymousObservableSink
,呼叫 sink 的on
,即
case .error,.completed:
if fetchOr(self._isStopped,1) == 0 {
self.forwardOn(event)
self.dispose() # 呼叫父類 Sink 的 dispose,父類呼叫 SinkDisposer 的 dispose
}
}
複製程式碼
那麼這時 SinkDisposer
就會對 sink 和 銷燬者1 ,進行銷燬
所以最後列印 銷燬者
1 的 閉包 -> Disposables 釋放
執行順序細節
這裡衍生出 Q 4
: 如果不顯 式 呼叫 onCompleted
呢? 改為
let obable = Observable<String>.create { observer -> Disposable in
observer.onNext("dispose")
// observer.onCompleted()
return Disposables.create {
print("Disposables 釋放")
}
}
let dispose = obable.subscribe(onNext: { (str) in
print("序列" + str)
},onError: { _ in
print("錯誤回撥")
},onCompleted: {
print("完成回撥")
},onDisposed: {
print("銷燬回撥")
})
# 顯式呼叫 dispose
dispose.dispose()
複製程式碼
這時候就會先呼叫 SinkDisposer
的dispose, 元祖 BinaryDisposable
大家還記得吧,SinkDisposer 會先呼叫,而銷燬者2
onDisposed 後呼叫
所以列印為 序列dispose -> Disposables 釋放 -> 銷燬回撥
Q 4 都有了 Q 5 還會遠嗎?
Q 5:
為什麼呼叫了 dispose 就可以銷燬 響應了呢?
因為 我們銷燬了 Sink
,銷燬了大廈,銷燬了通訊管道,銷燬了外賣小哥送外賣的地址
還怎麼響應呢 ?
當然了,序列 和 觀察者
,終究是 iOS 裡面的 平凡的例項物件
,會隨著控制器的生命週期而銷燬
聽到這裡
懂了的扣 1
不懂的扣腳
DisposeBag
RxSwift 還有一種垃圾回收方式, DisposeBag 可以稱之為 垃圾袋
可以把它想象成 一個 autoReleasePool
裡面裝滿了銷燬者
隨著你把 DisposeBag 扔進垃圾桶,也就是控制器的生命週期
結束
裡面的銷燬者也就逐一銷燬
rx.disposeBag
因為 NSObject+Rx
對 垃圾袋 進行了優雅的拓展 ,我們不需要自己建立 disposeBag 了
viewModel.title.asDriver()
.drive(titleLabel.rx.text)
.disposed(by: rx.disposeBag)
# 只需呼叫 rx.disposeBa 即可
# pod 'NSObject+Rx','~> 5.0' # https://github.com/RxSwiftCommunity/NSObject-Rx
複製程式碼
感興趣的可以自己看一下 rx.disposeBag 的原始碼
完
以上是我對 DisposeBag 的理解,還請不吝指正
希望你喝完這 2杯 mojito
味道的 RxSwift 之後
不再需要垃圾袋