RxSwift Удаление одной подписки вызывает удаление другой подписки

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?>. При инициировании просмотра для viewForHeaderInSectionя делаю подписку между infoMessageActionи 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запускается, infoDataкоторый, в свою очередь, запускает представление AlertViewController. Когда я прокручиваю представление заголовка за пределами экрана, подписка между view.infoMessageActionи infoDataудаляется (что является ожидаемым поведением, поскольку представление было определено).

Но я infoDataтакже избавляюсь от подписки между ViewController и. Я получаю event completedи disposeдля view.infoMessageAction<-> infoDataподписки , а также event completedи disposeдля infoData<-> ViewController подписки.

Я ожидаю, что только подписка view.infoMessageAction<-> infoDataдолжна прерваться. Также обе подписки удаляются разными disposeBag. Почему infoDataудаляется подписка <-> ViewController и как это предотвратить?

Заранее спасибо!

Ответы

DanielT. Aug 18 2020 at 18:28

Когда ваш FutureSpendingsHeaderViewдеинициализируется, любое представление, являющееся источником, infoMessageActionтакже деинициализируется, и это представление генерирует 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()
}

Теперь подписка удаляется по мере необходимости.