Register a function to be called just before program is quit - objective-c

So I need to register a function on OSX (using Xcode / objective C) that will register a particular function to be called whenever the program terminates.
I ran into this but i guess that's for iOS and not for OSX.
I replaced UI with NS and gave it a shot, but it did not work.
NSApplication *app = [NSApplication sharedApplication];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillTerminate:)
name:BeCalledOnExit object:app];
but that is not compiling. it says something on name: being an undeclared identifier when it is clearly on the .h & .m file before the function.
i ran into another chap who used this:
-(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *) sender{
return TRUE
}
but it doesnt work for me as my app is a complete status bar app.
Basically i'm creating some temp files during my app and just before quitting, i want to make sure that my app is clearing out these files. I am putting it in /tmp/.. Woudn't want too much space to be taken.
i would really like to have a solution like in gcc/python,
atexit(functionName);

Implement - (void)applicationWillTerminate:(NSNotification *)notification on your AppDelegate and it will be called just before the application will terminate
So the Implementation of your AppDelegate could look something like:
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
NSLog(#"I FINISHED LAUNCHING!");
}
- (void)applicationWillTerminate:(NSNotification *)notification
{
NSLog(#"I WILL TERMINATE NOW!!!");
}
#end

Your App Delegate should implement the - (void)applicationWillTerminate:(NSNotification *)notification to get the notification
(OR)
You can implement applicationShouldTerminate and perform clean of your files and return appropriate NSApplicationTerminateReply if your clean up fails

If you want to use a notification, you should be observing NSApplicationWillTerminateNotification.

NSApplication *app = [NSApplication sharedApplication];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillTerminate:)
name: NSApplicationWillTerminateNotification object:app];
/*A notification named NSApplicationWillTerminateNotification.*/
- (void)applicationWillTerminate:(NSNotification *)notification
{
}

Related

NSViewController -- dismiss with memory cleanup

My environment is Yosemite 10.10.5 with Xcode 7.2 using ARC.
In a simple test program, I am attempting various ways to dismiss a NSViewController and all of them are showing problems with memory handling.
In my primary view controller, I have the following code. (The notification pieces are there to test various ways of dismissing the presented controller.)
- (IBAction)showFirstReplacement:(id)sender {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissWithNotification:) name:#"removeFirst" object:nil];
NSStoryboard *sb = [self storyboard];
FirstReplacement *controller = [sb instantiateControllerWithIdentifier:#"first_replacement"];
[self presentViewControllerAsSheet:controller];
}
- (void)dismissWithNotification:(NSNotification *)notification {
NSViewController *controller = [notification object];
[self dismissViewController:controller];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Inside FirstReplacement, I have:
- (IBAction)dismiss:(id)sender {
[self dismissViewController:self];
// [[NSNotificationCenter defaultCenter] postNotificationName:#"removeFirst" object:self];
// [[self presentingViewController] dismissViewController:self];
}
Uncommenting any one of the three lines in this method produces the correct visual results but.... Depending on which of the calls I enable inside dismiss:, I get different results when profiling. Using self dismissViewController:, I see no leaks but FirstReplacement objects are not deallocated. Using either of the other two approaches gets rid of the dismissed FirstReplacement but leaks one 16-byte malloc block and one NSMutableArray every time a view controller is dismissed.
According to Instruments, the leaks are related to a method called [NSViewController _addPresentedViewController:].
Are there other clean-up steps necessary to prevent these leaks (or memory bloat in the non-leak case)?
The view controller that presents another view controller is also responsible for dismissing it. So none of the lines in FirstReplacement's dismiss method are correct. Instead, you should be creating a delegate in FirstReplacement so it can notify its delegate (the primary view controller) that it should be dismissed.
FirstReplacement.h
#class FirstReplacement;
#protocol FirstReplacementDelegate <NSObject>
- (void)firstReplacementShouldDismiss:(FirstReplacement *)controller;
#end
#interface FirstReplacement : NSViewController
#property (nonatomic, weak) id<FirstReplacementDelegate> delegate;
#end
FirstReplacement.m
- (IBAction)dismiss:(id)sender {
[self.delegate firstReplacementShouldDismiss:self];
}
Then in your primary view controller:
- (IBAction)showFirstReplacement:(id)sender {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissWithNotification:) name:#"removeFirst" object:nil];
NSStoryboard *sb = [self storyboard];
FirstReplacement *controller = [sb instantiateControllerWithIdentifier:#"first_replacement"];
controller.delegate = self;
[self presentViewControllerAsSheet:controller];
}
- (void)firstReplacementShouldDismiss:(FirstReplacement *)controller {
[self dismissViewController:controller];
}
While it may seem like posting a notification is the same as a delegate, it is not. The difference is that when dismissWithNotification fires, you are still executing the code from FirstReplacement::dismiss. NSNotificationCenter::postNotificationName does not finish executing until all observers have finished executing their selectors. So even though the dismissal code is executing in the primary view controller, it still being run from the dismiss method.
If you are still not convinced, override FirstReplacement::dealloc to print a log statement. You will see that dealloc is not called using any of your methods, but will be called using delegation.

Add observer for a process in Cocoa

I'm developing kind of a plugin for iTunes.
A lot of user have requested, that they would like to start the plugin if they start iTunes, which of course makes sense. However, I'm not sure how to do this.
I thought about a helper app, which is probably the only way.
The only thing that bothers me is how to get the notification.
Of course I could consistently check if iTunes is running, but I'm not sure if that's the right way to do it.
I would rather add my app as an observer of that process.
Is that possible?
If not, how does Activity Monitor do it?
SOLUTION
Thanks to Daij-Djan! I got it working like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(iTunesLaunched:)
name:NSWorkspaceDidLaunchApplicationNotification
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(iTunesTerminated:)
name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
}
-(void) iTunesLaunched:(NSNotification *)notification {
NSRunningApplication *runApp = [[notification userInfo] valueForKey:#"NSWorkspaceApplicationKey"];
if ([runApp.bundleIdentifier isEqualToString:#"com.apple.iTunes"])
NSLog(#"start");
}
-(void) iTunesTerminated:(NSNotification *)notification {
NSRunningApplication *runApp = [[notification userInfo] valueForKey:#"NSWorkspaceApplicationKey"];
if ([runApp.bundleIdentifier isEqualToString:#"com.apple.iTunes"])
NSLog(#"terminate");
}
register for NSWorkspace notifications:
NSWorkspaceDidLaunchApplicationNotification
NSWorkspaceDidTerminateApplicationNotification
see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSWorkspace_Class/Reference/Reference.html
there is also the possibility to KVO the runningApplications property
btw cocoatech has a nice NTRunningAppManager class that does just that

Is it possible to monitor other application using Cocoa on Mac?

For example, get the notification that another Application is becoming Active on the screen, or resign active state.
Sure. In your app delegate class, you can use NSWorkspace to get notified when an app becomes active (NSWorkspaceDidActivateApplicationNotification) or resigns active (NSWorkspaceDidDeactivateApplicationNotification). See the documentation on NSWorkspace for more info.
In your controller class, you'd do something like this:
- (id)init {
if ((self = [super init])) {
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(appDidActivate:)
name:NSWorkspaceDidActivateApplicationNotification
object:nil];
}
return self;
}
- (void)dealloc {
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
[super dealloc];
}
- (void)appDidActivate:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
NSLog(#"userInfo == %#", userInfo);
}
The key points are basically that you need to register to receive the notifications like shown in -init. You'd repeat the code to add another observer for each additional notification name that you want (e.g NSWorkspaceDidDeactivateApplicationNotification).
Another important thing to remember is to remove yourself as an observer in -dealloc (or elsewhere), so that NSWorkspace doesn't try to notify your controller object after it's been released+dealloc'd (and would no longer be valid).
In the specified -appDidActivate: method, do whatever you need to with the info about the app in question.
If you want something simpler than distributed objects, you could use distributed notifications from the distributed notification center. However, these are not posted unless you built the application. For monitoring when applications start or quit, you can use NSWorkspace and its notification center (suggested by NSGod)

NSNotificationCenter Scope definition

So I'm new to NSNotifications, I am wondering what the scope is. I.e. If I have an Application Delegate Class, and it is the receiver of a notification:
-(id)init
{
[ super init];
if (!self) return nil;
// Add to our notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveUpdateRequest:)
name:#"RequestStatusUpdate"
object:nil];
return self;
}
And has this method run on receive:
- (void) receiveUpdateRequest:(NSNotification *) notification
{
// Check the Notification Name
if ([[notification name] isEqualToString:#"RequestStatusUpdate"]){
NSLog (#"Recieved Update Status!");
}
else {
NSLog(#"Recieved Notification: %#",[notification name]);
}
}
Can I post a notification like so:
[[NSNotificationCenter defaultCenter] postNotificationName:#"RequestStatusUpdate" object:self];
From another object instance any where in my App?
Even for instance an object that instantiated by virtue of a NIB being loaded:
summaryWindow = [[SummaryWindowController alloc] initWithWindowNibName:#"SummaryWindow" owner:globalStatusController];
Do I have to have anything else configured in my summaryWindow Class to be able to call the postNotificationName method.
Or put a different way is the [NSNotificationCenter defaultCenter] global for all instances of all objects in my Application, I would assume thats how its suppose to work but currently when I call this method via an IBAction in my SummaryWindow , the notification does not seemed to be received.
I have tested both [NSThread currentThread] and the default Notification center and it does look like I'm in the thread and the same notification center ( which I think is always global). I am only looking into the thread thing as its come up on a few other threads.
2011-08-22 20:57:11.452 AppName[23102:1307] Using Default Notification Center: <CFNotificationCenter 0x10012c900 [0x7fff7d302ea0]>
2011-08-22 20:57:20.366 AppName[23102:1307] Using Default Notification Center: <CFNotificationCenter 0x10012c900 [0x7fff7d302ea0]>
Wow that was lame, I just found [[NSNotificationCenter defaultCenter] removeObserver:self]; in some earlier code. I had it in dealloc but some how managed to miss it in another NSTask method I was working on.
Or put a different way is the [NSNotificationCenter defaultCenter] global for all instances of all objects in my Application
Yes.
I would assume thats how its suppose to work but currently when I call this method via an IBAction in my SummaryWindow , the notification does not seemed to be received.
That's because you're registering in init. I'm betting this is Mac, and on Mac the application delegate is almost always instantiated from a nib file. You need to do this work in awakeFromNib.
Note that you generally do not need to check the notification's name. It's generally best to have a different method for each notification callback. You should also always create a string constant for your notification names. It's way too easy to mis-type them.

iphone SDK: Not sure why I am not receiving UITextField events?

I have defined the controller to receive the events.
#interface salesViewController : UIViewController
<UITextFieldDelegate>{
However, none of my events are not firing.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
//this is not getting called
}
In Interface Builder I assigned the TextField delegate to the salesView.
What am I missing?
You have to set the delegate properly. You observe the protocol, but you need to do this:
#interface YourController : UIViewController<UITextFieldDelegate> {
IBOutlet UITextField* field;
}
#end
#implementation YourController
-(void)viewDidLoad
{
[field setDelegate:self];
}
And you will receive the events. Alternatively, you can set the delegate in Interface Builder as well, along with doing it programmatically in loadView, allocating the field and setting the delegate.
Additionally, try to use NSNotificationCenter as little as possible. Notifications are somewhat obsolete unless there isn't really a direct path between you and the object in question. Just a small comment on the answer above.
what are you trying to accomplish? textFieldDidBeginEditing is messaged whenever the user selects the text field. If you are trying to update a label or something as the user makes edits, you need to setup an observer w/ NSNotificationCenter and watch for the notification that is fired whenever this happens.If you take this approach, make sure to remove the observer once you are done with it
for example:
#pragma mark
#pragma mark -
#pragma mark Notification Observers
- (void)addObservers {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldDidChange:) name:#"UITextFieldTextDidChangeNotification" object:nil];
}
- (void)removeObservers {
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"UITextFieldTextDidChangeNotification" object:nil];
}
if you need to keep tabs on multiple text fields, do something like this for your selector:
- (void)textFieldDidChange:(NSNotification*)aNotification {
UITextField *textField = (UITextField *)[aNotification object];
if([textField isEqual:usernameTextField])
{
[user setUsername:usernameTextField.text];
}
else if([textField isEqual:phoneNumberTextField])
{
[user setPhoneNumber:phoneNumberTextField.text];
}
}