Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion Groups
General
GeneralPortable MacsHardwareNetworking
Applications
Mac ApplicationsEudoraFirefox / MozillaInternet ExplorerOutlook ExpressMS OfficeEntourageExcelPowerPointWordVirtual PCMedia PlayerOther MS Products
Programming
Mac ProgrammingCodeWarriorPerl
Country Specific
Australian Mac GroupUK Mac Group

Mac Forum / Programming / Mac Programming / April 2008



Tip: Looking for answers? Try searching our database.

Cocoa Gotcha: NSApplicationMain never returns

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
David Phillip Oster - 18 Apr 2008 21:52 GMT
There is an old feature of Cocoa, not well documented, that bit me
recently:

When you create a new project, Apple gives you a main() that looks like
this:
-----------------
#import <Cocoa/Cocoa.h>

int main(int argc, const char *argv[]){
 return NSApplicationMain(argc, argv);
}
-----------------

I'm often tempted to add a little code here, like so:
(toy example)

-----------------
int main(int argc, const char *argv[]){
 NSLog(@"now %@", [NSDate date]);
 return NSApplicationMain(argc, argv);
}
-----------------

But that puts a message on the console:

-----------------
... *** _NSAutoreleaseNoPool(): Object 0x3042d0 of class NSCFDate
autoreleased with no pool in place - just leaking
-----------------

So I make the obvious fix to make the message go away and get:

-----------------
int main(int argc, const char *argv[]){
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 NSLog(@"now %@", [NSDate date]);
 int retVal = NSApplicationMain(argc, argv);
 [pool release];
 return retVal;
}
-----------------

But, a quick test shows that "now", below, is never executed.
-----------------
int main(int argc, const char *argv[]){
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 NSLog(@"then %@", [NSDate date]);
 int retVal = NSApplicationMain(argc, argv);
 NSLog(@"now %@", [NSDate date]);
 [pool release];
 return retVal;
}
-----------------

So what's going on here? Well, if you set a breakpoint on exit(), and
just quit the program you'll see that the Quit menu command calls

[NSApplication terminate: ... ];

which in turn calls exit(). If you quit the program by logging out, the
Finder sends your program a "quit" appleEvent, you'll still go through
exit().

I was surprised by this behavior, since MFC, Powerplant, and Carbon all
normally cleanly terminate the runloop and return all the way out.

So how do we fix this?

1.) You can't. There are operations you might desperately want to do
after the user signals he wants to quit, but if the user does a force
quit, or if the machine crashes, those operations aren't going to happen.

2.) Rather than putting code in main(), you can put it in an
+(void)initialize; method of some class. Of course, you can't control
the order that initialize;s get called. If you need a guarantee that you
are run first, you are back to putting code in main() or a more
complicated solution.

3.) You can use atexit() to register a function handler that will be
called from a normal exit().

BUT: NSApplicationMain() does clean up ALL the autorelease pools before
calling exit(), including the one we made before we called
NSApplicationMain().

So the finished code is the rather peculiar looking:

-----------------
static void Finish(void) {
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 NSLog(@"now %@", [NSDate date]);
 [pool release];
}

int main(int argc, const char *argv[]){
 [[NSAutoreleasePool alloc] init];
 atexit(Finish);
 NSLog(@"then %@", [NSDate date]);
 return NSApplicationMain(argc, argv);
}
-----------------

I suppose the moral of the story is: don't try to do things in main in a
Cocoa program.
Sean McBride - 24 Apr 2008 02:39 GMT
> So what's going on here? Well, if you set a breakpoint on exit(), and
> just quit the program you'll see that the Quit menu command calls
[quoted text clipped - 7 lines]
> I was surprised by this behavior, since MFC, Powerplant, and Carbon all
> normally cleanly terminate the runloop and return all the way out.

It's an optimisation to improve quitting speeds, so say the Cocoa folk.

When you quit an app, -dealloc is also not called for all the various
live objects.

applicationWillTerminate: is often useful for situations like these.

Sean
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.