Observer never gets removed from NSNotificationCenter - ios

I'm adding a view controller as an observer for UIKeyboardWillShowNotification notification.
I have this code in my viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
And in my dealloc:
[[NSNotificationCenter defaultCenter] removeObserver:self];
The observer is not being removed even though dealloc is called when the view controller is closed. So when I open it for the second time, NSNotificationCenter will attempt to notify the old object, which is released, and the app crashes.
I have seen several questions here on StackOverflow about this particular problem, but non of the answers works for me.
I've tried removing the observer in viewWillDisappear and viewDidDisappear but the same problem happens.
I'm using ARC.

Have you tried this exact chunk of code in viewWillDisappear?
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
From your explanation I don't think the problem is with the removal of the observer.
Try to trigger the Observer from another viewcontroller. If it's not triggered you'll know the removal is successful and the problem occurs when you are adding the observer the second time.

Maybe try by specifying the parameter name that you have set before like this :
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];

Looks like the observer is getting set multiple times. Is your controller inherited from a class which also registers for same notification? That could lead to controller instance getting registered as observer more than once. As a workaround try this, in your controller class where you add the observer,
// Remove as observer first
[[NSNotificationCenter defaultCenter] removeObserver:self];
name:UIKeyboardWillShowNotification
object:nil];
// Then add
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
This will ensure that the observer is getting added only once.
Hope that helps!

[[NSNotificationCenter defaultCenter] removeObserver:self name:#"name" object:nil];
it works fine with me

Related

viewDidUnload alternative iOS

Which method can I use to catch the event when I move to another view?
What I have tried so far and didn't work: viewWillDisappear:, willMoveToParentViewController:, dealloc:
I am trying to unregister an observer for notifications.
The viewDidUnload method is now Deprecated.
If you want unregister an observer for notification, try in dealloc method:
E.g.
(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
//or
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"notifiactionName" object:nil];
}

I cannot remove observers that I register

I am registering some observers in my application to show a controller when timeout occurs:
for(Ad* ad in ads){
if(ad.published){
[ad resetTimer];
[[NSNotificationCenter defaultCenter] removeObserver:ad name:#"TouchBegan" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:ad selector:#selector(resetTimer) name:#"TouchBegan" object:nil];
}
}
In Ad class, I try to remove observers in dealloc:
-(void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"TouchBegan" object:nil];
}
But it seems that there still exists an observer after dealloc.
Ads array is a property of a Shop class:
#property(nonatomic, strong) NSArray<Ad>* ads;
How I can completely remove observers that I register?
It is safe to remove self as observer for all names in dealloc [[NSNotificationCenter defaultCenter] removeObserver:self];
If the observer is added to a view controller, I strongly recommend adding it in viewWillAppear and removing it in viewWillDisappear.
[[NSNotificationCenter defaultCenter] removeObserver:someObserver]; will remove even the super class observers which is highly unrecommended (except in dealloc because the object is unloaded) but in viewWillDisappear you should remove the observers one by one by using [[NSNotificationCenter defaultCenter] removeObserver:self name:#"TouchBegan" object:nil];

Posting nsnotification but observer is not hearing it

I am using NSNotifiationCenter, like a 1000 other times, and post a notification, but my observers are not hearing it? What gives?
=== THIS IS IN MY TABLEVIEW ===
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleSuccessSocket)
name:kNotificationServiceStartSuccessful
object:self];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleFailedSocketConnection)
name:kNotificationSocketFailedToConnect
object:self];
}
=== THIS IS IN MY SOCKETMANAGER (socket manager is a singleton if that matters) ===
-(void)willStartService {
....
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationServiceStartSuccessful object:nil];
....
}
I have debugged and viewDidLoad is being called in my view. Yes, my willStartService is being called.
You are registering the TableView only for notifications sent by itself. It should be..
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleSuccessSocket)
name:kNotificationServiceStartSuccessful
object:nil];
Try setting object:nil in your -addObserver:selector:name:object method calls. What you're doing is telling Notification Center to ONLY listen for notifications that come from the table view instance.
If you don't want to pass nil you'll have to pass an instance of your socket manager.
Check the Docs:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/nsnotificationcenter_Class/Reference/Reference.html#//apple_ref/occ/instm/NSNotificationCenter/addObserver:selector:name:object:

NSNotificationCenter: removeAllObserver for self works for several observing objects?

Say if I have several:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(notificationReceived:)
name:NotificationA
object:self.player];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(notificationReceived:)
name:NotificationB
object:self.player];
The objects are all self.player but for different notifications, in the end I do:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Is this fine or do I have to use the full method to remove observer for each notification? Currently I'm having issue when the view controller is unloaded but player is still playing in background.
Thanks
The docs say: "removeObserver: Removes all the entries specifying a given observer from the receiver’s dispatch table." So your method call is enough.

NSNotificationCenter can lead to bugs. Do you know more elegant solutions?

I can add observer twice (by accident) to the notification center and I will get notifications twice.
Is it possible to get only one notification? Do you know more elegant solutions?
I show you this example because this may lead to bugs.
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
}
- (void)keyboardDidShow:(NSNotification *)ntf
{
}
If you're not sure if you added the observer somewhere else, you can use the following code everytime you're adding an Observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:aName object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:aSelector name:aName object:nil];
This way you are removing the old one (if it existed) and adding a new one.
It's not 100% fail proof but it's a start. This could fail in Multi-Threaded apps where the calls are being made async or other unique situations.
You can also set an object to nil and then later use that object as if was still valid.
Not everything can be made fail safe.

Resources