objective-c should I remove observing NSWindowWillCloseNotification? - objective-c

I am watching my mac app's preferences window status with the following code in - (void)awakeFromNib
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(windowWillClose:)
name:NSWindowWillCloseNotification
object:self.prefsWindow];
And the method windowWillClose: is the following
- (void)windowWillClose:(NSNotification *)notification {
NSLog(#"Received Notification!");
[[self.customOptField window] makeFirstResponder:nil];
}
I'd like to continue to watch preferences window status while my app is working. i.e. Every time preferences window would be closed, I'd like to call windowWillClose: method.
Currently my code seems to work fine. But I wonder that should I remove observe NSWindowWillCloseNotification? If should be removed, when should I?
Any advice would be appreciated!!

EVERY notification you register, should be removed in the opposite call.
for awakeFromNib which is only called once after init, the opposite is dealloc. So remove it there!
- (void)awakeFromNib {
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(windowWillClose:)
name: NSWindowWillCloseNotification
object: self.prefsWindow];
}
...
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver: self
name: NSWindowWillCloseNotification
object: self.prefsWindow ];
}

You need to remove the observer before you relinquish your last strong reference to the specific window that you're observing, if you ever do, and before the observer object itself is deallocated.
In the code you showed, the observer is self and the window you're observing is self.prefsWindow. Assuming that prefsWindow is a strong property, do you ever clear it to nil or assign a new window to it? If so, you need to remove the observer before doing so. And you would presumably want to start observing the new window, too.
And, of course, you have to remove the observer in self's -dealloc method (as Daij-Djan says), for two reasons: 1) that object is the observer, and 2) the object will be releasing its last strong reference to the observed window.

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.

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.

NSNotificationCenter addObserver in subclass

I register for being notified in superclass (UIViewController) like so:
SuperClass.m
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(notification:)
name:#"Notification"
object:nil];
}
- (void)notification:(NSNotification *)notification {
// Do something for SuperClass with the notification
}
Now In subclass (subclass of SuperClass.m) I also listen for the same notification like so:
SubClass.m
- (void)notification:(NSNotification *)notification {
// Do something specific for SubClass with the notification
}
Is this an acceptable (codewise) way to deal with having a general behaviour when acting on a notification in a superclass and to having a more specific behaviour when acting on a notification in a subclass?
Usually when you want to allow more specific behavior in the subclass, while still maintaining the general behavior in the superclass, you have the subclass call super. For example, the -[UIViewController viewDidAppear:] documentation says:
You can override this method to perform additional tasks associated with presenting the view. If you override this method, you must call super at some point in your implementation.
So your notification setup is fine (although it's a bit weird to have a NSNotification object as a parameter to a method you expect to be overridden) — but you'll want to call [super notification:notification] to get the superclass's behavior as well.

Execute code when app reopens

I have a Single View Application. When I hit the home button and ‘minimise’ the application I want to be able to execute code when the user reopens it.
For some reason viewDidAppear and viewWillAppear do not execute when I minimise and reopen the application.
Any suggestions?
Thanks in advance
sAdam
You can either execute code in the app delegate in
- (void)applicationDidBecomeActive:(UIApplication *)application
or register to observe the UIApplicationDidBecomeActiveNotification notification and execute your code in response.
There is also the notification UIApplicationWillEnterForegroundNotification and the method - (void)applicationWillEnterForeground:(UIApplication *)application in the app delegate.
To hook up notifications add this at an appropriate point
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
Define a the corresponding method
- (void)didBecomeActive:(NSNotification *)notification;
{
// Do some stuff
}
Then don't forget to remove yourself an observer at an appropriate point
[[NSNotificationCenter defaultCenter] removeObserver:self];
Discussion
You most likely only want your viewController to respond to events whilst it is the currently active view controller so a good place to register for the notifications would be viewDidLoad and then a good place to remove yourself as an observer would be viewDidUnload
If you are wanting to run the same logic that occurs in your viewDidAppear: method then abstract it into another method and have viewDidAppear: and the method that responds to the notification call this new method.
This is because since Apple implemented "Multitasking", apps are completely reloaded when you start them again, just as if you had never closed them. Because of this, there is no reason for viewDidAppear to be called.
You could either implement
- (void)applicationWillEnterForeground:(UIApplication *)application
and do there what ever you want. Or you register for the notification UIApplicationWillEnterForegroundNotification in your view controller. Do this in viewDidLoad:
[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myAppWillEnterForeground)
name:UIApplicationWillEnterForegroundNotification object:nil];
And of course implement the specified selector and do there what you want.
I am not sure how the answer by #Paul.s performs the OP request since registering UIApplicationDidBecomeActiveNotification will be executed twice:
When launching the app
When application goes into the background
A better practice will be to decouple those events into 2 different notifications:
UIApplicationDidBecomeActiveNotification:
Posted when the app becomes active.
An app is active when it is receiving events. An active app can be said to have focus. It gains focus after being launched, loses focus when an overlay window pops up or when the device is locked, and gains focus when the device is unlocked.
Which basically means that all logic related to "when application launched for the first time"
UIApplicationWillEnterForegroundNotification:
Posted shortly before an app leaves the background state on its way to becoming the active app.
Conclusion
This way we can create a design that will perform both algorithms but as a decoupled way:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethodName1) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethodName2) name:UIApplicationDidBecomeActiveNotification object:nil];
This because you don't redraw your view. Use applicationWillEnterForeground in the AppDelegate instead. This should work fine for you.

How to simulate applicationWillResignActive message?

How can one simulate applicationWillResignActive to be called?
Locking screen, going to main menu, simulating phone call - none seemed to have helped.
In case I expect things that won't happen, let me tell you more: I subscribe to this message and when that happens hope that notification is sent to a method as listed below:
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name: UIApplicationWillResignActiveNotification object:app];
Your code is valid, it works for me. And locking the screen or hitting the home button will cause this notification to be posted.
*(One caveat to this is if your device does not support multitasking or if you have the "Application does not run in background" property set to yes in your *info.plist. In which case it will go straight to the "UIApplicationWillTerminateNotification" notification)
So barring that there there are two possiblities:
1) Your addObserver code is not being called, ie. It's in the wrong method.
To test try this:
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
NSLog(#"Observer added");
2) The observer method is not being called properly. Which requires the same method as the at selector.
To test try this:
-(void)applicationWillResignActive:(NSNotification *)notification{
NSLog(#"applicationWillResignActive");
}
Just as an additional point, if you want to see in action which of these notifications are called and when. In your AppDelegate class put the line NSLog(#"%#", NSStringFromSelector(_cmd)); in each of the -application... methods and it will log them when they are called. It's a nice hands on way to get to know them.