kban
kban

Reputation: 150

Handle any application closing in objective c

I want to execute my method when any application is closing. My code is:

@interface FO: NSObject
- (void)applicationKilled:(NSNotification*)notification;
- (void)appDidLaunch:(NSNotification*)notification; 
@end

@implementation FO
- (void)applicationKilled:(NSNotification*)notification {
    NSLog(@"success");
}

- (void)appDidLaunch:(NSNotification*)notification {
    NSLog(@"app info: %@", [notification userInfo]);
}  
@end

@implementation Main:NSObject
FO fo; 
NSString * filePath = "...MyPath";
NSString * application = "..MyApplication";
 int main(int argc, const char * argv[]) {
    fo = [[FO alloc]init];
    [Main MyMethod];
    while(1==1) {
      ...some code;
        }
        return 0;
 }

+(void) MyMethod {
    center = [[NSWorkspace sharedWorkspace] notificationCenter];
    [center addObserver:fo selector:@selector(appDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
    [center addObserver:fo selector:@selector(applicationKilled:) name:NSWorkspaceDidTerminateApplicationNotification
                                     object:nil];         
    [[NSWorkspace sharedWorkspace] openFile:filePath withApplication:application]; }
@end

However, appDidLaunch method is not firing, even if i'll open another application in finder. Also applicationKilled method is never firing. When i'm executing following code

[center postNotificationName:NSWorkspaceDidLaunchApplicationNotification
     object:self];

appDidLaunch method is firing OK. Where can be a problem? Should this methods be fired every time when some application is opened or closed?

Upvotes: 0

Views: 341

Answers (2)

Rob Napier
Rob Napier

Reputation: 299575

CRD is on the right track. You absolutely must have a runloop to receive this notification. For example:

@implementation Main : NSObject

- (void)applicationDidFinishLaunching:(NSApplication *)app {
    [Main MyMethod];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
        // ... The rest of your program ...
    });
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyDelegate *delegate = [Main new];
        [[NSApplication sharedApplication] setDelegate:delegate];
        [NSApp run];
    }
    return 0;
}

I've put "the rest of your program" into a dispatch_async because you must not block the main thread. The usual way that Mac apps work is not with a big while (YES) loop. The usual way is by registering for various events and then waiting from them to happen. That's what the run loop is for. But if you have to manage your own loop (you generally shouldn't, but if you must), then you need to move it off of the main queue.

Upvotes: 1

CRD
CRD

Reputation: 53010

Assuming you are using ARC and also guessing as the information you give seems to be incomplete:

In your updated question you show fo declared as a local variable of MyMethod. The method addObserver:selector:name:object: does not keep a strong reference to the observer. After MyMethod returns the local fo object will be reclaimed, you now have no observer to call methods on.

However, while the above would explain why your code doesn't work it wouldn't explain why your app does not crash - and you don't report that it crashes. Running the code you give above causes the app to crash. So it appears that you've missed some information out or at least not reported the crash.

Guess Two

You have no run loop.

Many parts of the framework rely on there being a run loop which dispatches incoming events to appropriate handlers - just type "run loop" into Xcode's help. If you create a standard application using Xcode's "Cocoa Application" template the run loop is created for you by the code in main.m.

Events produced by OS X when applications start and stop are dispatched by the run loop to framework handlers which produce the corresponding notifications. Without a run loop these system events will not be handled, so no notifications.

You have:

int main(int argc, const char * argv[])
{
   fo = [[FO alloc]init];
   [Main MyMethod];
   while(1==1)
   {
      ...some code;
   }
   return 0;
}

so unless "...some code" creates a run loop the system events will not be handled.

Write your project using the standard "Cocoa Application" template and, for example, put your call to setup the notification handlers in applicationDidFinishLaunching:.

HTH

Upvotes: 1

Related Questions