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.