NotificationCenter.default.addObserver keep getting called multiple times with Unwind Segue

1.8k Views Asked by At

I am using an show segue and an unwind segue to navigate between two iOS viewControllers, VC1 and VC2. In the viewDidLoad() of VC2 I make VC2 an observer. Here is my code:

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "buzzer updated"), object: nil, queue: OperationQueue.main) { _ in
        print("set beeperViewImage")
    }
}

Every time I use the unwind segue to go from VC2 back to VC1 the addObserver() gets called an additional time, e.g., on the fourth return segue addObserver is called 4 time; on the fifth segue, five times, etc. This behavior happens even when the app is sent to the background and recalled. It remembers how many segues happened in the previous session and picks up the count from there.

I have no problems with multiple calls in VC1, which is the initial VC.

I have tried to set VC2 to nil after unwind segueing.

Looking forward to any guidance.

2

There are 2 best solutions below

0
Rob On

This is undoubtedly a case where your view controllers are not getting released. Perhaps you have a strong reference cycle.

For example, consider this innocuous example:

extension Notification.Name {
    static let buzzer = Notification.Name(rawValue: Bundle.main.bundleIdentifier! + ".buzzer")
}

class SecondViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(forName: .buzzer, object: nil, queue: .main) { _ in
            self.foo()
        }
    }

    func foo() { ... }
}

If I then enter and leave this view controller three times and then click on the enter image description here “debug memory graph” button, I will see the following:

enter image description here

I can see three instances of my second view controller in the panel on the left and if they were properly deallocated, they wouldn’t show up there. And when I click on any one of them in that panel, I can see a visual graph of what still has a strong reference to the view controller in question.

In this case, because I turned on the “Malloc Stack” feature under “Product” » “Scheme” » “Edit Scheme...” » “Run” » “Diagnostics” » “Logging”, I can see the stack trace in the right most panel, and can even click on the enter image description here arrow button and be taken to the offending code:

code

In this particular example, the problem was that I (deliberately, for illustrative purposes) introduced a persistent strong reference where the Notification Center is maintaining a strong reference to self, imposed by the closure of the observer. This is easily fixed, by using the [weak self] pattern in that closure:

NotificationCenter.default.addObserver(forName: .buzzer, object: nil, queue: .main) { [weak self] _ in
    self?.foo()
}

Now, I don’t know if this is the source of the strong reference cycle in your case because the code in your snippet doesn’t actually reference self. Perhaps you simplified your code snippet when you shared it with us. Maybe you have something completely else that is keeping reference to your view controllers.

But by using this “Debug Memory Graph” button, you can not only (a) confirm that there really are multiple instances of your relevant view controller in memory; but also (b) identify what established that strong reference. From there, you can diagnose what is the root cause of the problem. But the code in your question is insufficient to produce this problem, but I suspect a strong reference cycle somewhere.

0
Pat Dolan On

Thank you all for your comments on my problem. Based on them, I started searching for what might be holding on to my VC2. Turns out that a call to read a bluetooth radio link in my VC2 viewWillAppear() was the culprit but I don't understand why:

self.radio?.peripheral?.readValue(for: (self.radio?.buzzerChar)!)

Everything works fine after removing the above line of code. Thanks again for pointing out in which direction to search.