views:

2444

answers:

6

Consider the following main() method which you would find most iPhone applications:

int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

In every iPhone app that I've run in Simulator with these (including several sample projects provided by Apple), the thread never exits UIApplicationMain() and any remaining code in main() is never executed. Is this expected behavior?

I have verified that statements after UIApplicationMain() never run by stepping through the code with a debugger. When the user stops an application (by hitting the "Home" button, for example), the resulting stack trace shows that [UIApplication _terminateWithStatus:] is eventually called. This function calls your application delegate's applicationWillTerminate: method. Once that finishes, [UIApplication _terminateWithStatus:] seems to kill/exit the thread.

Can someone confirm that this is how main() is supposed to work, or at least confirm the same behavior on their machine?

+1  A: 

After [pool release] there is nothing to log to?

Genericrich
that theory should be easy to check, just log before the release.
Logan Capaldo
I tested it using a debugger before posting to verify that the thread never truly exits the UIApplicationMain() function (i.e., NSLog() never executes). Probably should have mentioned that in the question.
Clint Harris
+1  A: 

trying using fprintf and see what happens

int main(int argc, char *argv[])
{
    /*
       ... 
     same as above
       ... 
    */
    [pool release];
    char file_name = "/tmp/log"

    FILE *file = fopen(file_name, "w");

    fprintf(file_name, "END\n");
}

and tell us what happens

I also thought the easiest way to check was to set a break point right at the return

in gdb do

b main.c:x

where x is the line number of the return statement

hhafez
Sorry, I should have mentioned in the question that I stepped through main() in a debugger to verify that the thread never exits UIApplicationMain(). It really appears that when you stop an iPhone app by hitting the "Home" button, the expected behavior is to just kill the thread before main() exits.
Clint Harris
well that just confirms that the theory of [pool release] preventing the logggin false.
hhafez
+1  A: 

Try:

int main(int argc, char *argv[])
{
    NSLog(@"Step 0");
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSLog(@"Step 1");
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    NSLog(@"Step 2");
    [pool release];
    NSLog(@"Step 3");
    return retVal;
}

It may be that the release of the pool is preventing further logging in which case you'd get step 2 but not step 3.

If step 2 isn't being printed, then it's almost certainly something wrong with UIApplicationMain - there's a chance that it doesn't return so put NSLog statements (step 1.1, Step 1.2, ...) at various points within it and run to find the last message logged.

Keep drilling down (Step 1.7.1, 1.7.2, .... 1.7.6.3.2, ...) - eventually, you'll track down the exact line (however deep in the call hierarchy) when the log messages stop being logged and that line will be your culprit (either "turning off" logging or exiting without returning normally).

One further snippet I found on the web:

=====

When you use this line:

int retVal = UIApplicationMain(argc, argv, @"MyApp", @"MyApp");

The first MyApp is your main app delegate class. The second is the class to where SpringBoard sends touch notifications.

Also, if you are using the SDK, and have a main nib defined in the Info.plist, then you can leave the call as:

int retVal = UIApplicationMain(argc, argv, nil, nil);

as all that will be covered when you create your xibs.

=====

Now I don't know enough about iPhone development (specifically xibs) to know what that last bit even means (or if you've set it up correctly) but it sounds like another phase of compilation.

However, my first thought from reading that is that Springboard will call your delegate class when the buttons are pressed to ask you to do something (like shut down gracefully). If it can't ask you (i.e., no delegate), it's probably within its rights to shut you down as it sees fit, such as with [UIApplication _terminateWithStatus:].

In the Windows world, you would probably send off a quit message to the main window but, as I say, iPhone development may be different.

Still, it's an avenue to investigate. I'd be interested in seeing what calls were made to a delegate if you provided one. The code included with the snippet above had this:

@implementation MyApp
- (void) applicationDidFinishLaunching:(id)unused {
    rect = [ UIHardware fullScreenApplicationContentRect ];
    rect.origin.x = 0.0f;
    rect.origin.y = 0.0f;
    window = [ [ UIWindow alloc ] initWithContentRect: rect ];
    [ window makeKeyAndVisible ];
    view = [ [ MyAppView alloc ] initWithFrame: rect ];
    [ window setContentView: view ];
}
- (void) dealloc {
    [ window release ];
    [ view release ];
    [ super dealloc ];
}

So maybe a delegate with dealloc() is the secret to getting it to exit back to main(). Why don't you give that a shot? It may get you closer to your goal even if it doesn't solve the core problem.

paxdiablo
I should have mentioned that I stepped through main() in a debugger to verify that the thread never exits UIApplicationMain(). That said, I also ran your test for the sake of clarity; "Step 2" and "Step 3" do not appear in the console.
Clint Harris
+1  A: 

After calling the UIApplicationMain function your application launches (establishing a runloop, etc) and all work should then be done outside the context of main (if you need it to run in main, do it before that point). When quitting an application it is generally more efficient to allow the OS to do memory cleanup.

wisequark
Agreed. My concern is not with the pool being released, however, but with the possibility that my own app is doing something wrong and not terminating correctly (based on Apple's docs, main() should finish). But you have a good point regarding shutdown/cleanup efficiency and avoiding dealloc calls.
Clint Harris
+8  A: 

The original question was: "Why doesn’t an iPhone app’s main() function ever get a chance to finish?"

Short Answer: Because UIApplicationMain() is coded such that it never returns.

After doing several tests in Simulator and on the device, and asking another developer to do the same tests, I have confirmed that UIApplicationMain never returns. When the user terminates an application normally by hitting the Home button, The program ultimately terminates inside an unpublished UIApplication method called _terminateWithStatus. This method calls exit(0).

This behavior matches that of the NSApplicationMain function (which is AppKit/Cocoa version of the UIApplicationMain function). The documentation for NSApplicationMain() clearly states that it will never return.

I have submitted a bug (6600198) to Apple requesting that the official documentation (and Xcode template for main.m) be corrected to state that UIApplicationMain() never returns. While this is not a functional problem, the current template and docs are misleading.

Thanks to everyone for all the input and brainstorming!

Clint Harris
Update: I'm happy to report that Apple modified the official documentation for UIApplicationMain() and closed my bug. The documentation now includes the following: Even though an integer return type is specified, this function never returns. When users terminate an iPhone application by pressing the Home button, the application immediately exits by calling the exit system function with an argument of zero."
Clint Harris
A: 

I have that not return experience too. And have set break points to verify exactly like Clint said.

wisequark has a good point.

great topic. makes me feel more comfortable that i am not the only one who has the question.