iOS Remove observer from notification: Can I call this once for all observers? And even if there are none?
ios, nsnotificationcenter, objective-c
Solution
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.
Update for iOS 9 / macOS 10.11 and later
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.
Problem
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]; } ```