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)
}
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
.