RxSwiftでデータバインド -補足編-
このエントリは、Qiitaに投稿した記事の補足です。
上記の記事では、RxSwiftを使った双方向データバインドの
実現方法をまとめていますが、データバインドの解除方法についても
少し整理して書いておきたいと思います。
1. データバインドを解除しないと困るケース
自分が遭遇した困るケースは、UITableViewとのデータバインド......。
UITableViewの中で扱われるUITableViewCellのインスタンスは使い回しされます。
例えば、7行表示のテーブルでは、Cellのインスタンスは7つだけ作成され
画面スクロール時も当該インスタンスのプロパティを更新する動きになります。
そのため、CellとViewModelのインスタンス(Array)をバインドすると
ViewModelとControllerの1対1のバインド関係が崩壊してしまい
ViewModelを更新したタイミングで意図しないCellが書き換えられたりします。
1行目のCellとバインドされたViewModelを更新すると1行目のCellを流用した
○行目のCellがなぜか更新されてしまう......なんて感じですね。
対策として、非表示になった行については
データバインドを解除してやることでViewModelとControllerのバインド
関係の整合性を保ちます。この方法を採ることで、データバインドされている
Cellは画面に表示されている部分だけ......という状況を作っています。
2. データバインド解除する方法
簡単です。
データバインドの処理を書いたときのことを思い出します。
/** データバインド設定 */ private func bindViewAndModel() { textField.rx_text.subscribeNext { [unowned self] text in self.viewModel.text = text }.addDisposableTo(bag) }
上記コードのsubscribeNext関数はDisposable型インスタンスを返却します。
(返却されたインスタンスを即座にDisposableBagに突っ込んでますが......)
このDisposable型インスタンスとは何か?という点がポイントになります。
Disposableは”バインドしたよ!”という保証書だと理解すると簡単です。
RxSwiftではDisposable(保証書)が有効である限りデータバインドを行います。
逆にいうとDisposableを失効させればバインドを解除できます。
Disposableクラスは、dispose関数を提供しているので実行しましょう。
/** データバインド設定 */ private func bindViewAndModel() { let disposable = textField.rx_text.subscribeNext { [unowned self] text in self.viewModel.text = text } // データバインド解除 disposable.dispose() }
ね?簡単でしょう。
3. DisposeBagとは何か?
Disposableが何か分かると、DisposeBagの役目も分かります。
class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! let viewModel = ViewModel() let bag = DisposeBag() // 中略 }
Disposableインスタンスを保持するのがDisposeBagです。
その役目は即ち
”deinitのタイミングで保持している全Disposableを失効させる”
ことです。
上記のコードで言うと
画面破棄のタイミングで自動的にデータバインドが解除されます。
明示的にデータバインド解除を行わない限りは
DisposeBagを活用するのがベストプラクティスになると思います。
(解除を忘れると怖いですし......)