【RxSwift】DisposeBagクラスの使い方!Disposable
この記事からわかること
- RxSwiftで購読を廃棄するDisposeとは?
- DisposeBagクラスの使い方
- Disposableクラスとは?
- .disposed(by:)メソッドの意味
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
RxSwiftの購読を廃棄するDisposeの概要と使い方などをまとめていきます。
おすすめ記事:【RxSwiftとは?導入方法と使い方まとめ!ストリームを理解する
RxSwiftのDisposeとは?
RxSwiftではObservableオブジェクトを購読(観測を開始)することでイベント通知を取得して処理を実装していました。これには観測されるストリームと観測する側(Observer)が必要です。
しかし対象のストリームをいつまでも観測し続けているとメモリリークの原因を引き起こす可能性があります。そのためにはどこかのタイミングで観測を停止する必要があります。それを「Dispose(廃棄)」と呼びます。
メモリリークとは?
メモリリークとは確保したメモリ領域を解放し忘れることによりメモリが確保され続けた状態になってしまうプログラムバグの1種です。
メモリは有限であり、使用すればするほど動作が重くなったり予期せぬ挙動を引き起こす原因になります。 Observableオブジェクトを購読開始後、観測が不要になったタイミングで廃棄処理を行うことが適切なメモリ管理を行う上で重要になります。
廃棄方法
観測しているObservableオブジェクトでは明示的に実行しなくてもonErrorまたはonCompletedが呼ばれイベントが終了した際に自動で廃棄処理を行ってくれます。
しかし両者が呼ばれない場合などは廃止処理を実装する必要があります。
実際に廃棄処理を行うのはDisposable
の持つdispose
メソッドです。
ですがdispose
メソッドを手動で呼び出すことは公式より推奨されておらずDisposeBag
クラスやtakeUntil
メソッドなどを使用した方法が推奨されています。
”Note that you usually do not want to manually call dispose; this is only an educational example. Calling dispose manually is usually a bad code smell. There are better ways to dispose of subscriptions such as DisposeBag, the takeUntil operator, or some other mechanism.”
まだ自動で廃止処理が行われる場合でも確実にリソースを解放できるように廃止処理は記述しておくことが重要です。
”Using dispose bags or takeUntil operator is a robust way of making sure resources are cleaned up. We recommend using them in production even if the sequences will terminate in finite time.”
DisposeBagクラス
公式リファレンス:RxSwift/Disposables/DisposeBag.swift
RxSwiftでは廃棄処理のためのDisposeBag
クラスが用意されています。このクラスを使用することで登録した購読処理を一括で廃棄することができます。よく「ゴミ箱」と表現されますがまさしく「溜めて捨てる」という役割を果たしてくれます。
DisposeBagクラスが一括で購読を破棄するタイミングはdeinit(デイニシャライザ)が呼ばれる時です。
実際の使い方
ここではTextFieldの入力イベントを観測しLabelのテキストにバインディングさせる場合を例に実用的な使い方を見てみます。 TextFieldの入力イベントではonNextしか呼ばれないので明示的に廃棄する必要があります。
ステップ1:DisposeBagインスタンスの生成
まずはDisposeBagインスタンスをViewControllerクラスのプロパティとして定義しておきます。ゴミ箱を設置したイメージです。
let disposeBag = DisposeBag()
ステップ2:disposedメソッドで登録
subscribe
メソッドの返り値はDisposable
オブジェクトなのでここからdisposed
メソッドを呼び出します。このメソッドの引数にDisposeBagオブジェクトを渡すことでゴミ箱との紐付けが完了します。
text.subscribe(onNext: { [weak self] text in
label.text = text
}).disposed(by: disposeBag)
ステップ3:DisposeBagの実行タイミング
DisposeBagの実行タイミングはデイニシャライザが呼ばれた時なのでViewControllerクラスが解放されたタイミング(画面遷移など)に実行されます。
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.frame = CGRect(x:130, y:200,width:200, height:50)
label.text = "placeholder..." // textField.text とBindingされるので表示されることはない
self.view.addSubview(label)
let textField = UITextField()
textField.frame = CGRect(x:130, y:300,width:200, height:50)
textField.borderStyle = UITextField.BorderStyle.line
self.view.addSubview(textField)
let text = textField.rx.text.asObservable() // Observableクラスの取得
text.subscribe(onNext: { [weak self] text in
label.text = text // 変化した値をUIとリンク
}).disposed(by: disposeBag)
}
}
参考文献:ReactiveX/RxSwift/Documentation/GettingStarted.md
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。