1. 程式人生 > Android開發 >RxSwift: 麻煩給我的愛人來一個 DisposeBag

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

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

不再需要垃圾袋