I'm registering three observers in most of my view controllers. Some have more, some less but I want to include part of the registration and unregistration process in a parent class. Is there any problem with calling the unregistering even if there is no observer? And is one call to unregister enough for all three observers?
- (void)registerForKeyboardNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterBackground:) name:UIApplicationWillResignActiveNotification object:nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; //Has to be unregistered always, otherwise nav controllers down the line will call this method [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }
specifies. If your app targets iOS 9.0 and later or macOS 10.11 and later, and you used addObserver(_:selector:name:object:) , you do not need to unregister the observer. If you forget or are unable to remove the observer, the system cleans up the next time it would have posted to it.
Use the method deinit . In Objective-C classes I would always remove NSNotificationCenter observers in the -dealloc method, but a Swift class doesn't have a -dealloc method. Instead, Swift has a deinit method.
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.
Yes, that will remove all registrations where the observer is self
. It's documented in the NSNotificationCenter Class Reference:
The following example illustrates how to unregister
someObserver
for all notifications for which it had previously registered:[[NSNotificationCenter defaultCenter] removeObserver:someObserver];
Note that in theory (but not, as far as I know, in practice as of iOS 7.0), UIViewController
could have its own registrations that it doesn't want removed in viewWillDisappear:
. It's unlikely to register for any of the notifications in the public API using addObserver:selector:name:object:
, because that would preclude you registering for them in your UIViewController
subclass, but it could certainly register for non-public notifications now or in a future version.
A safe way to deregister is to send removeObserver:name:object:
once for each registration:
- (void)deregisterForKeyboardNotifications { NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [center removeObserver:self name:UIKeyboardWillHideNotification object:nil]; [center removeObserver:self name:UIApplicationWillResignActiveNotification object:nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self deregisterForKeyboardNotifications]; } - (void)dealloc { [self deregisterForKeyboardNotifications]; }
Another way is to use addObserverForName:object:queue:usingBlock:
to register (instead of addObserver:selector:name:object:
). This returns a new observer object reference for each registration. You have to save these away (perhaps in an NSArray
instance variable if you don't want to create individual instance variables). Then you pass each one to removeObserver:
to deregister its notification. Example:
@implementation MyViewController { NSMutableArray *observers; } - (void)registerForKeyboardNotifications { NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; NSOperationQueue *queue = [NSOperationQueue mainQueue]; __weak MyViewController *me = self; observers = [NSMutableArray array]; [observers addObject:[center addObserverForName:UIKeyboardWillShowNotification object:nil queue:queue usingBlock:^(NSNotification *note) { [me keyboardWillShow:note]; }]]; [observers addObject:[center addObserverForName:UIKeyboardWillHideNotification object:nil queue:queue usingBlock:^(NSNotification *note) { [me keyboardWillHide:note]; }]]; [observers addObject:[center addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:queue usingBlock:^(NSNotification *note) { [me applicationWillResignActive:note]; }]]; } - (void)deregisterForKeyboardNotifications { for (id observer in observers) { [[NSNotificationCenter defaultCenter] removeObserver:observer]; } observers = nil; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self deregisterForKeyboardNotifications]; } - (void)dealloc { [self deregisterForKeyboardNotifications]; }
Since every observer returned by addObserverForName:object:queue:usingBlock:
is a new object that has only one registration, each call to removeObserver:
is guaranteed to only remove that observer's one registration.
As of iOS 9 and macOS 10.11, NSNotificationCenter
automatically deregisters an observer if the observer is deallocated. It is no longer necessary to deregister yourself manually in your dealloc
method (or deinit
in Swift) if your deployment target is iOS 9 or later or macOS 10.11 or later.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With