RxSwift 1つのサブスクリプションを破棄すると、別のサブスクリプションの破棄が呼び出されます

Aug 18 2020

私が持っているPublishSubject<InfoData>のViewControllerに。そして私はそれを購読しているので、それがイベントを発行するとき-私はUIAlertViewControllerを表示します。

let infoData = PublishSubject<InfoData>()
private func bindInfoData() {
     infoData.subscribe(onNext: { [weak self] (title, message) in
         self?.presentInfoSheetController(with: title, message: message)
     }).disposed(by: disposeBag)
}

ViewControllerには、セクションヘッダー付きのtableViewがあります。セクションヘッダービューにはinfoMessageAction: PublishSubject<InfoData?>。があります。のビューを開始するときにviewForHeaderInSectioninfoMessageActionとの間でサブスクリプションを作成しますinfoData

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
      let view = FutureSpendingsHeaderView(frame: frame)
      view.infoMessageAction
            .compactMap { $0 }
            .bind(to: infoData)
            .disposed(by: view.disposeBag)
      return view
}

ヘッダービューが初めて開始されると、すべて正常に機能します-infoMessageActionトリガーされ、infoDataAlertViewControllerの表示がトリガーされます。ヘッダービューを画面を超えてスクロールするview.infoMessageActionと、サブスクリプションとinfoDataディスポーズが行われます(これは、ビューが定義されたときに予想される動作です)。

しかし、infoDataとViewControllerの間のサブスクリプションも破棄されます。私は、受信event completeddisposeのためのview.infoMessageAction< - >infoDataサブスクリプションともevent completeddisposeのためのinfoData< - >のViewControllerのサブスクリプション。

view.infoMessageAction<->infoDataサブスクリプションだけが壊れることを期待しています。また、両方のサブスクリプションが異なるdisposeBagによって破棄されました。infoData<-> ViewControllerサブスクリプションが破棄されるのはなぜですか?それを防ぐ方法は?

前もって感謝します!

回答

DanielT. Aug 18 2020 at 18:28

あなたは、ときFutureSpendingsHeaderViewの源であるものは何でもビュー、deinitializedさinfoMessageActionもdeinitialized、およびそのビューが放出されcompleted、その時点でイベントを。その完了したイベントは渡され、infoDataその後、独自の完了したイベントが発行されます。

Observableが完了したイベントを発行すると、それが実行されます。これ以上イベントを発行することはできません。したがって、サブスクリプションは破棄されます。

あなたの答え@Alexは、ビュー内のものが非初期化される順序を変更することによって方程式を変更します。disposeBagは最初に非初期化され、ビューが完了したイベントを送信する前に監視可能なチェーンを切断します。

より良い解決策は、PublishRelayではなくを使用することですPublishSubject。リレーは完了したイベントを発行しません。

それよりもさらに良いのは、主題を完全に取り除き、次のようなことをすることです。

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let view = FutureSpendingsHeaderView(frame: frame)
    view.infoMessageAction
        .compactMap { $0 }
        .subscribe(onNext: { [weak self] (title, message) in
            self?.presentInfoSheetController(with: title, message: message)
        })
        .disposed(by: view.disposeBag)
    return view
}
Alex Aug 18 2020 at 16:20

誰かがそのような状況に直面した場合、問題を見つけました。セクションヘッダービューではdisposeBag、定数として開始し、ビューが終了すると、他のすべてはRxSwift自体によって処理されると考えました。そこで、ビューを次のように更新しました。

var disposeBag = DisposeBag()
    
deinit {
    disposeBag = DisposeBag()
}

これで、サブスクリプションは必要に応じて破棄されます。