Adding 2 observer with one name in NSNotificationCenter in 1 class - objective-c

I have a observer problem with NSNotificationCenter in my app.
My AppDelegate class has 2 service class to get data by url which called ExhibitionService & NewsService.
This 2 service class uses one Queueloader class in itself.
When I wrote 2 observer to listen service load operations in my appdelegate class, it returns error and crashes.
APP DELEGATE CLASS
ExhibitionLoaderService *exhibitionService = [[ExhibitionLoaderService alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(exhitibionServiceComplete :) name:**CserviceComplete** object:nil];
[exhibitionService load];
NewsLoaderService *newsService = [[NewsLoaderService alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(newsServiceComplete :) name:**CserviceComplete** object:nil];
[newsService load];
ExhibitionLoaderService.m & NewsLoaderService has the same method
-(void)load
{
Queueloader *que = [[Queueloader alloc] initWithPath:CExhibitionURLPath isVerbose:NO];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didQueComplete:) name:CdidQueueloaderComplete object:nil];
[que startOperation];
[que release];
}
ERROR I GOT
[[NSNotificationCenter defaultCenter] postNotificationName:**CdidQueueloaderComplete** object:results];
2 service class has CdidQueueloaderComplete... Problem is about observers but how? what?
PS. Program received signal EXC_BAD_ACCESS.
Thanks.

There's no problem with having multiple observers of the same notification. The problem you describe sounds a lot like it's related to the lifetime of your observers.
If you deallocate an object that's still registered to listen to notifications, the NSNotificationCenter doesn't know about that. If a notification comes in in the future, the center will forward it to the object it thinks is still listening (but that has gone away), and you get a crash.
The solution to this problem is to ensure that your object is removed as an observer before it is destroyed. There are two ways to do this:
often you'll know when an object should start or stop listening to notifications, and you can make sure you remove it as an observer when it should stop (for example, perhaps view controllers should start listening for model updates when their views appear and stop listening when their views disappear)
other times, an object can look after its own notification lifecycle: perhaps you can start listening in an initialiser and stop listening in -dealloc.
Whatever way you do it, you need to balance adding observers and removing observers so that when an object goes away it's no longer registered with the notification center.

Related

Notifications being removed

I have a model object and a window controller. I would like them to be able to communicate via notifications. I create both during the App Delegate's -applicationDidFinishLaunching: method. I add the observers after the window controller's window is loaded, like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
WordSetWindowController* windowController = [[WordSetWindowController alloc] initWithWindowNibName:#"WordSetWindowController"];
model = [[WordSetModel alloc] init];
NSWindow* window = windowController.window;
[[NSNotificationCenter defaultCenter] addObserver:windowController
selector:#selector(handleNotification:)
name:kNotification_GeneratingPairs
object:model];
[[NSNotificationCenter defaultCenter] addObserver:windowController
selector:#selector(handleNotification:)
name:kNotification_ProcessingPairs
object:model];
[[NSNotificationCenter defaultCenter] addObserver:windowController
selector:#selector(handleNotification:)
name:kNotification_UpdatePairs
object:model];
[[NSNotificationCenter defaultCenter] addObserver:windowController
selector:#selector(handleNotification:)
name:kNotification_Complete
object:model];
[model initiateSearch];
}
The -iniateSearch method kicks off some threads to do some processor-intensive calculations in the background. I'd like those threads to send notifications so I can update the UI while processing is occurring. It looks like this:
- (void)initiateSearch;
{
[[NSNotificationCenter defaultCenter] postNotificationName:kNotification_GeneratingPairs
object:self];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ... do the first part of the calculations ...
// Notify the UI to update
self->state = SearchState_ProcessingPairs;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kNotification_ProcessingPairs
object:self];
});
// ... Do some more calculations ...
// Notify the UI that we're done
self->state = SearchState_Idle;
dispatch_sync(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kNotification_Complete
object:self];
});
});
}
The first notification works properly, but none of the notifications that happen in the dispatch_async() call ever cause the notification handler to be called. I've tried calling -postNotificationName:: both on the background thread and the UI thread (both by using dispatch_async(dispatch_get_main_queue(),...) and by calling -performSelectorOnMainThread:::) and neither had any effect.
Curious, I added a call via an NSTimer that waits 5 seconds after the big dispatch_async() call at the end of -initiateSearch: and found that even though that all occurs on the main UI thread, it also does not fire the notification handler. If I simply call postNotification::: immediately after the dispatch_async() call returns, it works properly, though.
From this, I'm concluding that the observers are somehow getting removed from the notification center, despite the fact that my code never calls -removeObserver:. Why does this happen, and how can I either keep it from happening or where can I move my calls to -addObserver so that they aren't affected by this?
Is suspect your window controller is getting deallocated.
You assign it to WordSetWindowController* windowController, but that reference disappears at the end of -applicationDidFinishLaunching:, and likely your window controller with it.
Since the window itself is retained by AppKit while open, you end up with an on-screen window without a controller. (Neither NSWindow nor NSNotificationCenter maintain strong references to its controller/observers.)
The initial notification works because those are posted before -applicationDidFinishLaunching: ends and/or the autorelease pool for that event is drained.
Create a strong reference to your window controller in your application delegate, store the window controller's reference there, and I suspect everything will work as advertised.
Something very similar happened to me with a window controller that was also a table delegate; the initial setup and delegate messages would work perfectly, but then later selection events mysteriously disappeared.

NSNotification listening

I have multiple instances of NSWindowController, who is registering for a notification to listen whenever edit happens.
Now when I edit something from one instance of windowcontroller, the notification gets posted and all the instances of that NSWindowcontroller listen to that notification,but I want only the instance which has updated its details to listen.
How can I achieve that?
As mentioned in the documentation for [NSNotificationCenter addObserver:selector:name:object:] method for parameter object:
The object whose notifications the observer wants to receive; that is,
only notifications sent by this sender are delivered to the observer.
If you pass nil, the notification center doesn’t use a notification’s
sender to decide whether to deliver it to the observer.
So just pass self as object.
E.G.
Registering for notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(someSelector:)
name:#"SomeNotification"
object:self]; // <- SELF!!
Posting notification:
[[NSNotificationCenter defaultCenter] postNotificationName:#"SomeNotification"
object:self //<- SELF!!
userInfo:nil];

Objective-C keyboard methods unload keyboardwillappear? Is that necessary?

Given this code in the viewDidLoad:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(keyboardWillShow:) name:
UIKeyboardWillShowNotification object:nil];
[nc addObserver:self selector:#selector(keyboardWillHide:) name:
UIKeyboardWillHideNotification object:nil];
I'm asking myself if I need to delete the observers when I unload the view (or something similar).
It looks a bit like this question, but that question does not discuss if dealloc is deprecated since the use of ARC (edit: see comments in accepted answer).
But since ios updates all the time and I have no clue if you still would need to call dealloc and I've never seen a piece of code how to do this (delete the observers that is), some help would be appreciated :)
Yes, you do. Override dealloc in your view controller like so:
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
In general, you should remove an observer from a notification center when the observer is no longer needed -- either you don't need it to continue observing, or the object itself is being deallocated. You don't need to remove an object observing UIKeyboardWillHideNotification simply because the keyboard is going away, but you do need to remove it if you're getting rid of the object itself.
Consider what would happen if you didn't remove the observer before it was deallocated: if the notification it was watching for ever occurred, the notification center would try to send a message to that object. That would cause either a crash or some behavior that you don't expect, since the pointer the notification center used to send the message no longer points to the object that's supposed to observe the notification.
It is always good to have your notification observer's unregistered while they are not required.
Since your observer deals with Keyboard which is a visual element, have it unregistered, i.e. removed from observer on viewDidDisappear and have it re-register in viewWillAppear (something which i follow.).
This is required coz, in case you are pushing a new view controller over the one you are using, the observer will still be registered and can cause erroneous behaviour, and sometimes, if your view controller get deallocated, the notification will still have the observer registered and can lead to crash. Coz notification centre defaultCenter is a singleton instance for process and will be there for its lifetime.
Post iOS 6.0 you can call it in viewDidUnload. But iOS 6.0 that is deprecated.

Send notification to another view controller when request finishes

I am working on an Iphone Application (ios5 + storyboard + arc).
I have 2 ViewControllers A and B.
In A I have a button. when pressed I am submitting a request asynchronously to the server (using AFNetworking) and I will go to View Controller B by using performSegueWithIdentifier (push not modal).
When the request finishes it executes a request successful Block that will save data to the database. (The block is in ViewController A since the request is sent from there)
Is there a way I can notify ViewController B that the request has finished and execute a method in B?
What I'm looking for is that when the request finishes and enters the Success Block I run a method in view controller B which is the loaded view.
I hope I was clear.
Thanks
For posting a notification use the below code:
[[NSNotificationCenter defaultCenter] postNotificationName:#"Midhun" object:nil];
In the viewDidLoad of the notification listening class add observer like:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(performTask:) name:#"Midhun" object:nil];
performTask: is the method which will be called when the notification is observed.
Please refer NSNotification Class Reference
First options is to store reference to view controller B somewhere (as example in application delegate) and use it to run a method.
The second one is to send notification via [NSNotificationCenter defaultCenter], so controller B set a listener to a notification somewhere (viewDidLoad can be a good place):
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(finished:) name:#"requestfinishes" object:nil];
and controlle A sends it:
[[NSNotificationCenter defaultCenter] postNotificationName:#"requestfinishes" object:nil userInfo:nil];
Note that if you send a notification from a different thread, listener will be executed on the same thread.

nsnotification for specific object

Im working on an ios app, ios 5+ , using xcode and objective c. Ok currently messing with nsnotifications and i just need some clarifications as im a wee bit confused.
Lets say i have a view controller that i add an observer to like so
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(showContent:) name:kTPSShowContentNotification object:self];
where the object is set to self. I took this to mean that it is only looking from that notification if sent from that object. am i wrong on that?
elsewhere in code I am positing a notification like so
[[NSNotificationCenter defaultCenter] postNotificationName:kTPSShowContentNotification object:currentVC];
where currentVC is the view controller that has the observer set up initially.
I thought this is all that was needed to catch that notification as the post is telling the notification center to send it from that view controller. but it fails to catch it and im unsure as to why. If when adding the observer i set the object as nil then it catches it but so does all the other viewcontrollers (if any ) that have observers for that notification too. Is there any way around this ? am i approaching this completely wrong?
To receive notification only from theObjectSendingNotification object you should write:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(showContent:) name:kTPSShowContentNotification object:theObjectSendingNotification];
and the object sending notification should send it in this way
[[NSNotificationCenter defaultCenter] postNotificationName:kTPSShowContentNotification object:self];
If I'm getting this right, you want to post and get a notification from the same controller. So you can do something like this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(showContent:) name:kTPSShowContentNotification object:self];
[[NSNotificationCenter defaultCenter] postNotificationName:kTPSShowContentNotification object:self];
But it should work indeed if your currentVC ivar is pointing to the very same controller. The fact that you say it doesn't work makes me believe that it is not pointing to the same instance of your controller.