dispose()とdisposed(by: DisposeBag)を使いこなす

はじめに

前回PublishSubjectについての記事を書いてみたが、解説用のミニマムサンプルsubscribeしている部分でdisposeを理解していなかったので、勉強し直しました!

disposeって何?

subscribeで処理を購読している部分でよく見かけるdisposeですが、なくてもおそらく問題なく動くはずです。

しかし、disposeをしないといつまでもsubscribeしている状態にが残り、メモリリークしてしまいます。

dispose()の例

subscribeの後ろにdispose()をつけると購読がすぐに解除されます。

前回のPubishSubjectを理解する記事のコードがもしこのような感じになっていたとしたらどうなるでしょうか?

.dispose()は一度処理(ストリーム)が流れたらすぐに購読をやめて破棄してしまうので、logih.end()で処理を流しても購読されず、ラベルの文字がendに変わりません。

override func viewDidLoad() {
        super.viewDidLoad()
        
        //PublishSubjectに変更があったらここに来る
        logic.stateChanged.observeOn(MainScheduler.instance).subscribe(onNext: {[weak self] state in
            self?.changeLabelText(state: state)
            }).dispose() // ←ここがdispose()に変わっています!!!!!!

        //上の処理が呼ばれる
        logic.start()

        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            //上の処理が呼ばれる
            self.logic.end()
        }
        
    }

disposed(by: DisposeBag)って何?

disposed(by: DisposeBag)は引数として受け取ったDisposeBagが解放される時に購読を解除するものです。

基本的にはクラスのメンバに保持しておいて、インスタンスが破棄される時購読解除をする使い方をされます。

disposed(by: DisposeBag)間違えた例

ミニマムサンプルを作成していた際に凡ミスをしていてストリームが1回しか流れず困っていました、その時のコードがこちらです。

    override func viewDidLoad() {
        super.viewDidLoad()
        let disposeBag = DisposeBag()

        //PublishSubjectに変更があったらここに来る logic.stateChanged.observeOn(MainScheduler.instance).subscribe(onNext: {[weak self] state in
            self?.changeLabelText(state: state)
            }).dispose()

        //上の処理が呼ばれる
        logic.start()

        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            //上の処理が呼ばれる
            self.logic.end()
        }

どこがミスかわかりましたか?

このコードの問題はDisposeBag()をviewDidLoadの中で宣言してしまっていたので、DispatchQueueが呼ばれる前に一度DisposeBag()をインスタンスかした変数が解除されてしまい、処理が1度しか流れなくなってしまっていました。

disposed(by: DisposeBag)正しい使い方

クラスのメンバとしてDisposeBag()を保持していればインスタンスが破棄される際にsubscribeの購読が解除されます。

スコープには気をつけましょう!!!!

    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //PublishSubjectに変更があったらここに来る
        logic.stateChanged.observeOn(MainScheduler.instance).subscribe(onNext: {[weak self] state in
            self?.changeLabelText(state: state)
            }).dispose()

        //上の処理が呼ばれる
        logic.start()

        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            //上の処理が呼ばれる
            self.logic.end()
        }
        
    }