I have two view that when you switch from one to another, they call a notification to the view that's about to get loaded to refresh the content. The weird thing is that the first time the view loads, it will call it once, the next time twice, and so on. I concluded that it's because they keep getting added every time the view loads. Since the dealloc never get's called it's still there and it will keep adding now.
So is there a way to check if the notification exists before getting added to fix this issue?
Here's what I have in my viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ReloadGridNotification:) name:#"ReloadOHGridView" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ReloadBadgeNotification:) name:#"reloadBadge" object:nil];
And my dealloc:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Thanks!Coulton
EDIT 1:
I show my views in a UINavigationController and switch between them. Here's my code to refresh the different view:
- (void)viewWillDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadBadge" object:self];
}
Every time you call addObserver, the notification center will add a entry to its internal structures. This means the notification center will call your observer once more every time you call viewDidLoad.
If your view is unloaded for any reason and then reloaded, then viewDidLoad will get called again. Your removeobserver will not get called until the object is destroyed, which may explain why your removeobserver did not work.
You should either check whether you have already called addObserver with a flag, or manually remove the observer with removeObserver when you unload your view in the viewDidUnload method.
Edit1: Alternatively, can you add the observers somewhere else, like in the App Delegate?
When memory gets tight the OS will dump your view, but will first call viewDidUnload; when it has to reload them it calls viewDidLoad. dealloc, however, is only called when all references to your view have been released, which likely doesn't happen until your app quits.
As a result, as #futureelite7 noted, you're adding a new observer every time your view is reloaded but, effectively, are never removing it.
All you need to do is ensure that the observer is added in viewDidLoad and removed in viewDidUnload you won't have the multiple notification problem. No need for a flag or for putting the observer anywhere else.
From your comments it sounds like you might have tried it, but I suggest going through your code and making absolutely certain you're only adding them in DidLoad and are always removing them in DidUnload. It works like a charm in app after app.
Edited to Add
Because your view keeps getting unloaded, which only happens if you do it manually, if all references to it are lost, or if memory gets tight, I suggest looking at all three to help ensure you're doing what you can to keep your view around.
Related
I have the following method in my GameWindowController (subclass of NSWindowController):
- (void)windowWillClose:(NSNotification *)notification {
AppDelegate *delegate = [NSApp delegate];
[delegate removeGameWindowController:self];
}
The code for removeGameWindowController in AppDelegate is:
- (void)removeGameWindowController:(GameWindowController*)controller {
[self.controllers removeObject:controller];
}
self.controllers is an NSMutableArray with all my GameWindowControllers.
The above code seems to have a race condition. It will randomly crash with EXC_BAD_ACCESS when I close windows, almost every time if I close all windows at once.
My guess is that ARC is deallocating the window controller before or as removeGameWindowController: returns, leaving the window with a dangling pointer to the controller. I have tried adding controller.window.windowController = nil; to no avail.
For some reason, using the (BOOL)windowShouldClose:(id)sender delegate method instead as suggested in https://stackoverflow.com/a/11782844/344544 works, but is not an acceptable solution as it is not called upon quit.
How can I reliably remove my window controllers from the array of controllers after each window has closed? Is there some other delegate method which gets called or some NSNotification I can subscribe to which fire after a window has finished closing?
After lengthy investigation and step by step running in the debugger I figured out the source of the problem and a possible solution.
The window controller was indeed being released at some point after the end of removeGameWindowController: along with all its strong references which include the NSWindow. If the window was released before the stack had unwound back to the close call on the window itself, the program would crash while finishing the that function as self in this particular case is a dangling pointer.
I was unable to find a way to get notified after a window had closed, however it is likely such an approach would have had the exact same problem.
In order to ensure no reference to the window was left anywhere on the stack I queued the removal of the window controller from the array to happen as a subsequent event on the runloop:
- (void)removeGameWindowController:(GameWindowController*)controller {
[self.controllers performSelectorOnMainThread:#selector(removeObject:) withObject:controller waitUntilDone:NO];
}
Having discovered that awakeFromNib is begin called multiple times, I tried to implement loadView in the following way to prevent (nib loading) initialization from repeatedly occurring, with:
- (void)loadView {
[self viewWillLoad];
[super loadView];
[self viewDidLoad];
}
Looks like a good trick to allowing certain arrays and properties to be set-up in viewWillLoad, but loadView absolutely won't be called.
Why?
I've done much research about this here and through google.
You're not receiving a loadView message because you have this VC and its view in the same nib, with the VC's view outlet set to the view. Since the VC already has a view, it has no reason to go load another one.
loadView is typically not called if you are using a nib (since view is already set). But the real question is why you're trying to fight the view loading process this way. If awakeFromNib is being called multiple times, that suggests you have multiple instances of this class. Each will get a call to awakeFromNib (that is expected behavior). If this is surprising, you should dig into why you have multiple instances. But you shouldn't try to subvert the view-loading mechanism like this.
I use cocoalibspotify in an application and would like to get notified when a playlist is added or deleted.
I have tried adding an observer for the key path userPlaylists on the shared session but this does not seem to get called. I have also tried implementing the -sessionDidChangeMetadata: delete method but this seems to be only called when logging in.
Does anyone know how to get notified when the user adds or deletes a playlist?
You need to add a KVO observer to the playlists property of your session's userPlaylists container. You were adding your KVO one step too short. Note that the userPlaylists property will be nil for a short time after logging in, so you need to watch for that change too:
self.session = [SPSession sharedSession];
[self addObserver:self forKeyPath:#"session.userPlaylists.playlists" options:0 context:nil];
I am trying to use ScriptingBridge to write a small iTunes controller. The problem is to find an efficient way of getting notifyed whenever any changes occur. My first approch was to poll the input in a loop and just keep checking for differences. But I think there must be a more efficient way of getting notifyed about input!
Thanks in advance!
iTunes sends out a notification when something changes so just register for it in your init method of AppDelegate. Here's an example...
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:#selector(receivediTunesNotification:) name:#"com.apple.iTunes.playerInfo" object:nil];
The actual notifcation object in your method "receivediTunesNotification:" will contain information about the changes.
I have a MAAttachedWindow that shows itself when a status bar item is clicked. I need to have it close when its clicked outside of. I found some other directions that say to set it as a delegate and use - (void)windowDidResignKey:(NSNotification *)notification to detect when the user exits the window. I've tried it many times but can't seem to get it working which is probably because I didn't correctly set the delegate. Whats the best way to set the delegate so it will respond to the notification? The code is available here.
Thanks in advance
I found out to get the notification you have to register for it.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(windowDidResignKey:)
name:NSWindowDidResignKeyNotification
object:self];