RxSwift: 麻煩給個人愛人來一個 DisposeBag


上篇文章 mojito: 麻煩給個人愛人來一份 RxSwiftgit

沒有說起 DisposeBag 回收機制github

緣由有其二api

一是由於安全

時間過短,我沒來及看bash

二是由於閉包

文章過長,必口腔潰瘍ide


Disposable

話很少說,來的都是回頭客post

看一下經典案例fetch

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 類型 ?ui

先理清一些概念

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

fetchOrisFlagSet 都是 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,將生成的 sinksubscription分別持有

並返回 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: AnonymousObservableSinkSinkDisposer 之間的關係是什麼?

A 3: SinkDisposer 將本身的實例,經過匿名觀察序列,傳入 管道 AnonymousObservableSink 中,管道 Sink 擁有最終解釋權


意味着當外界執行訂閱subscribe的時候, 會creat 一個 Disposables 元祖

Disposables 元祖內包含 :

  • onDisposed 銷燬者2
  • 管道清潔工 SinkDisposer
    • sink
      • AnonymousObservableSink
    • subscription
      • 銷燬者1


由於加入了 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,即 執行 subscribeonCompleted閉包,而且接着 調用

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 以後

再也不須要垃圾袋

相關文章
相關標籤/搜索