Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to removeObserver in Swift 5 using addObserver closure method

This is my first post. I'm Japanese iOS engineer (just became this month).

I have a trouble with removeObserver method of NotificationCenter in Swift 5.

I added observer to ViewController (VC) by using closure type addObserver. I want to remove this Observer when VC's deinitialization has called.

I wrote NotificationCenter.default.removeObserver(self) in VC's deinit method. But, this seemed not to work for me.

What's the problem???

Additionally, if my code has memory leak problem, let me know how to fix it.

Here's piece of my code.

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: nil) { [weak self] notification in

            guard let self = self else { return }
            self.loadWeather(notification.object)
        }
    }
    
    deinit {
        print(#function)
        print("ViewController died")

        NotificationCenter.default.removeObserver(self)
    }
}
like image 664
Daichi Hayashi Avatar asked Apr 21 '20 13:04

Daichi Hayashi


Video Answer


1 Answers

Closure-based addObserver

If you use the closure-based variant of addObserver, the token (not self) is the observer. The notification center has no knowledge at all about self in this situation. The documentation is not super clear on this.

from Twitter

Meaning it's meaningless to do: NotificationCenter.default.removeObserver(self)

The docs recommend two ways:

Normal way

Subscribe as you normally do:

let center = NSNotificationCenter.defaultCenter()
let mainQueue = NSOperationQueue.mainQueue()
self.localeChangeObserver = center.addObserverForName(NSCurrentLocaleDidChangeNotification, object: nil, queue: mainQueue) { (note) in
    print("The user's locale changed to: \(NSLocale.currentLocale().localeIdentifier)")
}

Remove the observer at some point of code. NotificationCenter.default.removeObserver(self.localeChangeObserver) e.g. through a function or in deinit

Single subscribe

Remove the observer immediately after the first time it gets a callback

let center = NSNotificationCenter.defaultCenter()
let mainQueue = NSOperationQueue.mainQueue()
var token: NSObjectProtocol?
token = center.addObserverForName("OneTimeNotification", object: nil, queue: mainQueue) { (note) in
    print("Received the notification!")
    center.removeObserver(token!)
}

Selector-based addObserver

If you use the selector-based add, self (there is no token) the observer. Having that said, you should avoid doing:

NotificationCenter.default.removeObserver(self)

because your code may not be the only code adding observers that involve the object. When removing an observer, remove it with the most specific detail possible. For example, if you used a name and object to register the observer, use removeObserver(_:name:object:) with the name and object.

It’s only safe to call removeObserver(something) in the deinit method, other than that don’t use it use removeObserver(_:name:object:) instead.

Calling removeObserver(self) is incorrect outside deinit, because you’d be removing all observations set for the object. Calling it inside deinit isn’t incorrect, but meaningless, because the object is to be deallocated immediately.

like image 86
mfaani Avatar answered Oct 04 '22 09:10

mfaani