0

I recently noticed that none of my subscriptions are actually disposed even though I hook .disposed(by: disposeBag). Even if I leave the view or dismiss the modal completely, the subscriptions refuse to dispose.

I have my views in a tabBar which keeps the subscriptions alive I suppose, even though I leave the view, but even so, this tabBar is in a modal and when the modal is dismissed shouldn't the subscriptions dispose on their own accord?

One way around this is to manually dispose all subscriptions on viewWillDisappear, but I would still like to know why this issue persists.

private func noErrorText() {
    viewModel.activeErrorContent.debug("--debugNoErrorText").subscribe(onNext: { [weak self] cells in
        self?.noErrorView.isHidden = !cells.isEmpty 
    }).disposed(by: disposeBag)
}

Which gives the output:

2022-03-25 04:26:55.219: --debugNoErrorText -> subscribed 2022-03-25 04:26:55.219: --debugNoErrorText -> Event next([])

Let me know if there is anything else that I should provide or explain.

EDIT In response to the comments:

The disposeBag is in a superClass and my subscriptions and disposed(by:) are in a subClass. Not sure if that's relevant.

final class TechnicianDashboardViewController: BaseViewController {...

Here are my subscriptions:

If I understand it correctly with strong references, then self.disposeBag in the first snippet creates a strong reference to the subView.

extension TechnicianDashboardViewController: PinCodeViewControllerDelegate, UITableViewDelegate {
    func completed(currentPin: String?, newPin: String) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
            guard let self = self else { return }
            self.resetWithOverlay(pin: newPin)
                .subscribe().disposed(by: self.disposeBag)
        }
    }
}

Then there are these as well. All use the same disposeBag. None of them are disposed.

private func noErrorText() {
        viewModel.activeErrorContent.subscribe(onNext: { [weak self] cells in
            self?.noErrorView.isHidden = !cells.isEmpty
        }).disposed(by: disposeBag)
    }
    
private func getErrors() {
    viewModel.activeErrorContent.bind(to: activeErrorTableView.rx
                                            .items(cellIdentifier: ErrorsTableViewCell.identifier,
                                                   cellType: ErrorsTableViewCell.self)) { row, data, cell in
                                                       cell.rowService = row
                                                       cell.viewModel = data
    }.disposed(by: disposeBag)
}
    
private func getEvents() {
    viewModel.activeEventContent.bind(to: activeEventTableView.rx
                                            .items(cellIdentifier: EventStatusTableViewCell.identifier,
                                                   cellType: EventStatusTableViewCell.self)) { row, data, cell in
                                                       cell.viewModel = data
                                                       cell.rowService = row
    }.disposed(by: disposeBag)
}
Joakim Sjöstedt
  • 824
  • 2
  • 9
  • 18
  • 1
    When the objects that have a strong reference to the dispose bag deinit, the dispose bag *will* dispose the subscriptions. Which objects have a strong reference to your dispose bag? – Daniel T. Jun 02 '22 at 11:11
  • @DanielT. Ah, ok. When you say "objects that have a strong reference to the disposebag", do you mean all subscriptions that use .disposed(by: disposeBag)? Or how do I know what objects have a strong reference? Is there a way to check that? Could you elaborate a bit? :) – Joakim Sjöstedt Jun 03 '22 at 20:24
  • 1
    The `disposeBag` is a property of some class. Maybe you passed it to some other objects? No the `.disposed(by: disposeBag)` does not create a strong reference. – Daniel T. Jun 03 '22 at 20:31
  • @DanielT. Then setting the disposeBag as weak could be a valid solution? – Joakim Sjöstedt Jun 07 '22 at 09:35
  • 1
    No, that would be a horrible idea. Show the code or answer my question. Which objects have a strong reference to your dispose bag? – Daniel T. Jun 07 '22 at 11:11
  • @DanielT. Ok, so I updated my answer above. I guess the first part with self.disposeBag creates a strong reference, is that correct? – Joakim Sjöstedt Jun 09 '22 at 09:02
  • 1
    No, that doesn't create a strong reference. Based on what you have shown so far (assuming all the code you have shown is in the view controller,) when `TechnicianDashboardViewController` deinits, all the subscriptions would be disposed. Is your view controller deinit-ing? – Daniel T. Jun 09 '22 at 11:21
  • So I put a print in `deinit` in `TechnicianDashboardViewController` and it triggers first thing, even before `viewDidLoad`. That doesn't seem right... – Joakim Sjöstedt Jun 13 '22 at 10:37

1 Answers1

0

So I figured out the cause and solution to why the subscriptions don't dispose, much thanks to @Daniel T. I'll write the answer here in case someone have a similar problem.

So basically I added a print to deinit like this:

deinit {
    print("-- DEINIT")
}

And it turns out that it's not triggered at all (Well, they are triggered right at the start before anything is loaded, but not when you exit the view and I think this is because of the views being in a tabBar controller, but that's another topic). And this goes for several views. This means that the subscriptions aren't disposed, because that happens in the deinit.

So the next question is, why isn't deinit triggered. That is because there's a memory leak preventing the ARC to release the reference to the view. The memory leak can be caused by different things, but for most of the time it's because you forget to add [weak self] to a closure. If you just forget it at a single place, you can block deinit from triggering. In my case, my mistakes were often "hidden" in the super class, making it less obvious.

So, fixing the memory leak so that deinit trigger, will then dispose your subscriptions like normal, preventing even further memory leaks.

Joakim Sjöstedt
  • 824
  • 2
  • 9
  • 18