RxSwift Membuang satu langganan memanggil buang langganan lain

Aug 18 2020

Saya memiliki PublishSubject<InfoData>di ViewController. Dan saya berlangganan, jadi ketika itu memancarkan acara - saya tunjukkan 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)
}

Dalam ViewController saya memiliki tableView dengan header bagian. Tampilan tajuk bagian memiliki infoMessageAction: PublishSubject<InfoData?>. Saat memulai tampilan untuk viewForHeaderInSectionsaya membuat langganan antara infoMessageActiondan 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
}

Ketika tampilan header dimulai untuk pertama kalinya, semuanya berfungsi dengan baik - infoMessageActionmemicu infoDatayang pada gilirannya memicu presentasi AlertViewController. Ketika saya menggulir tampilan tajuk di luar layar, langganan antara view.infoMessageActiondan infoDatamembuang (yang merupakan perilaku yang diharapkan sebagai tampilan itu deinited).

Tapi saya juga membuang langganan antara infoDatadan ViewController. Saya menerima event completeddan disposeuntuk view.infoMessageAction<-> infoDataberlangganan dan juga event completeddan disposeuntuk infoData<-> ViewController berlangganan.

Saya berharap bahwa hanya view.infoMessageAction<-> infoDatalangganan yang rusak. Juga kedua langganan dibuang oleh disposeBag yang berbeda. Mengapa infoDatalangganan <-> ViewController dibuang dan bagaimana cara mencegahnya?

Terima kasih sebelumnya!

Jawaban

DanielT. Aug 18 2020 at 18:28

Saat Anda FutureSpendingsHeaderViewdideinisialisasi, tampilan apa pun yang merupakan sumbernya infoMessageActionjuga sedang dideinisialisasikan, dan tampilan tersebut memancarkan completedperistiwa pada saat itu. Acara selesai itu diteruskan infoDatayang kemudian memancarkan acara selesai sendiri.

Setelah Observable mengeluarkan event yang sudah selesai, itu selesai. Itu tidak bisa memancarkan acara lagi. Jadi langganannya dibuang.

Jawaban Anda @Alex mengubah persamaan dengan mengubah urutan hal-hal di dalam tampilan Anda dideinisialisasi. DisposeBag mendapatkan deinisialisasi terlebih dahulu, yang memutus rantai yang dapat diamati sebelum tampilan mengirimkan peristiwa yang telah selesai.

Solusi yang lebih baik adalah menggunakan a PublishRelaydaripada a PublishSubject. Relai tidak memancarkan acara yang sudah selesai.

Bahkan lebih baik dari itu adalah menyingkirkan subjek sepenuhnya dan melakukan sesuatu seperti:

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

Menemukan masalahnya, jika ada yang menghadapi situasi seperti itu. Di bagian tampilan tajuk, saya memulai disposeBagsebagai konstan dan berpikir bahwa segala sesuatu yang lain ditangani oleh RxSwift itu sendiri ketika tampilan dideinited. Jadi saya memperbarui tampilan menjadi:

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

Sekarang langganan dibuang sesuai kebutuhan.