简体   繁体   中英

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

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

Set your observer object to current view controller.

From apple doc.s , object is

The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer.

NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification,
                                       object: self,
                                       queue: nil) { [weak self] notification in
    guard let self = self else { return }
    self.loadWeather(notification.object)
}

Removing observer from NotificationCenter

deinit {
    NotificationCenter.default.removeObserver(self)
}

ANOTHER WAY

You can also make copy of Notification Observer object and remove it from NotificationCenter in deinit .

let notificationCenter = NotificationCenter.default
var loadWeatherObserver: NSObjectProtocol?

override func viewDidLoad() {
    super.viewDidLoad()
    loadWeatherObserver = notificationCenter.addObserver(forName: UIApplication.didBecomeActiveNotification,
                                                         object: nil,
                                                         queue: nil) { [weak self] notification in
        guard let self = self else { return }
        self.loadWeather(notification.object)
    }
}

deinit {
    if (loadWeatherObserver != nil) {
        notificationCenter.removeObserver(loadWeatherObserver!)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM