RxSwift Descartar uma assinatura invoca a disposição de outra assinatura
Eu tenho um PublishSubject<InfoData>
em um ViewController. E eu me inscrevo nele, então quando ele emite um evento - eu mostro o 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)
}
Em um ViewController, tenho um tableView com cabeçalhos de seção. A visualização do cabeçalho da seção possui um infoMessageAction: PublishSubject<InfoData?>
. Ao iniciar uma visualização para viewForHeaderInSection
, faço uma assinatura entre infoMessageAction
e 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
}
Quando a visualização do cabeçalho é iniciada pela primeira vez, tudo funciona bem - infoMessageAction
aciona o infoData
que, por sua vez, aciona a apresentação de AlertViewController. Quando eu rolar a exibição do cabeçalho além da tela, a assinatura entre view.infoMessageAction
e infoData
descarta (o que é o comportamento esperado quando a exibição foi definida).
Mas eu descarto a assinatura entre infoData
e ViewController também. Eu recebo event completed
e dispose
para assinatura view.infoMessageAction
<-> infoData
e também event completed
e dispose
para infoData
assinatura <-> ViewController.
Espero que apenas view.infoMessageAction
<-> a infoData
assinatura seja interrompida. Além disso, ambas as assinaturas disponibilizadas por diferentes disposeBag. Por que a infoData
assinatura do <-> ViewController foi descartada e como evitá-lo?
Desde já, obrigado!
Respostas
Quando o seu FutureSpendingsHeaderView
é desinicializado, qualquer visão que seja a fonte infoMessageAction
também está sendo desinicializada e essa visão emite um completed
evento naquele momento. Esse evento concluído é passado para o infoData
qual então emite seu próprio evento concluído.
Uma vez que um Observable tenha emitido um evento completo, está feito. Não pode emitir mais eventos. Portanto, a assinatura para ele é descartada.
Sua resposta @Alex muda a equação, alterando a ordem em que as coisas dentro de sua visualização são desinicializadas. O disposeBag está sendo desinicializado primeiro agora, o que quebra a cadeia observável antes que a visualização envie o evento concluído.
Uma solução melhor seria usar um em PublishRelay
vez de um PublishSubject
. Os relés não emitem eventos concluídos.
Ainda melhor do que isso seria se livrar totalmente do assunto e fazer algo como:
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
}
Encontrou o problema, se alguém enfrentar tal situação. Na visão do cabeçalho da seção, iniciei disposeBag
como constante e pensei que todo o resto é tratado pelo próprio RxSwift quando a visão é definida. Então, atualizei a visualização para:
var disposeBag = DisposeBag()
deinit {
disposeBag = DisposeBag()
}
Agora, a assinatura é descartada conforme necessário.