为什么iPhone应用程序的main()函数没有机会完成?

时间:2009-02-18 01:25:55

标签: objective-c iphone

考虑以下main()方法,您可以找到大多数iPhone应用程序:

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

在我在Simulator中运行的每个iPhone应用程序(包括Apple提供的几个示例项目)中,线程永远不会退出UIApplicationMain(),并且main()中的任何剩余代码永远不会被执行。这是预期的行为吗?

我已经验证了UIApplicationMain()之后的语句永远不会通过使用调试器逐步执行代码来运行。当用户停止应用程序时(例如,通过点击“Home”按钮),生成的堆栈跟踪显示最终调用[UIApplication _terminateWithStatus:]。此函数调用应用程序委托的applicationWillTerminate:方法。一旦完成,[UIApplication _terminateWithStatus:]似乎杀死/退出线程。

有人可以确认这是main()应该如何工作,或者至少在他们的机器上确认相同的行为?

6 个答案:

答案 0 :(得分:19)

最初的问题是:“为什么iPhone应用程序的main()函数没有机会完成?

简答:因为UIApplicationMain()编码使得它永远不会返回。

在Simulator和设备上进行了多次测试,并要求其他开发人员进行相同的测试后,我确认UIApplicationMain永远不会返回。当用户通过点击Home按钮正常终止应用程序时,程序最终会在名为_terminateWithStatus的未发布的UIApplication方法内终止。此方法调用exit(0)。

此行为与NSApplicationMain函数(UIApplicationMain函数的AppKit / Cocoa版本)相匹配。 NSApplicationMain()的文档明确指出它永远不会返回。

我向Apple提交了一个错误(6600198),要求更正官方文档(以及main.m的Xcode模板)以声明UIApplicationMain()永远不会返回。虽然这不是一个功能性问题,但当前的模板和文档会产生误导。

感谢大家的所有投入和头脑风暴!

答案 1 :(得分:3)

尝试:

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;
}

可能是池的发布阻止了进一步的记录,在这种情况下你会得到第2步而不是第3步。

如果没有打印第2步,那么UIApplicationMain几乎肯定有问题 - 它有可能不会返回,所以将NSLog语句(步骤1.1,步骤1.2,......)放在其中的各个点并运行以查找记录的最后一条消息。

继续向下钻取(步骤1.7.1,1.7.2,...... 1.7.6.3.2,......) - 最终,您将追踪确切的线(无论在调用层次结构中有多深)日志消息停止记录,该行将成为您的罪魁祸首(“关闭”日志记录或退出而不正常返回)。

我在网上找到了另一个片段:

=====

使用此行时:

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

第一个MyApp是您的主要应用程序委托类。第二个是SpringBoard发送触摸通知的类。

此外,如果您使用的是SDK,并且在Info.plist中定义了主nib,那么您可以将呼叫保留为:

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

因为创建xib时将涵盖所有内容。

=====

现在我对iPhone开发(特别是xibs)了解不足以了解最后一点甚至意味着什么(或者如果你已经正确设置了它),但这听起来像是另一个编译阶段。

但是,我首先想到的是,当按下按钮时,Springboard会调用你的委托类来要求你做某事(比如优雅地关闭)。如果它不能问你(即没有代表),它可能会在你认为合适的情况下关闭你,例如[UIApplication _terminateWithStatus:]

在Windows世界中,您可能会向主窗口发送退出消息,但正如我所说,iPhone开发可能会有所不同。

不过,这是一个调查的途径。我有兴趣看看如果你提供了一个代表就给代表打了什么电话。上面代码段中包含的代码包含:

@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 ];
}

因此,使用dealloc()的委托可能是让它退回main()的秘密。你为什么不试一试?即使它无法解决核心问题,它也可能让您更接近目标。

答案 2 :(得分:1)

在[池发布]之后,没有什么可以登录?

答案 3 :(得分:1)

尝试使用fprintf并查看会发生什么

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");
}

告诉我们会发生什么

我还认为最简单的检查方法是在返回时设置一个断点

在gdb中执行

b main.c:x 

其中x是return语句的行号

答案 4 :(得分:1)

调用UIApplicationMain函数后,应用程序启动(建立runloop等),然后所有工作都应该在main的上下文之外完成(如果你需要它在main中运行,那么在那之前做) 。退出应用程序时,允许操作系统进行内存清理通常更有效。

答案 5 :(得分:0)

我也没有回报经验。并设置了断点来验证克林特所说的。

wisequark有一个好点。

很棒的话题。让我感觉更舒服,我不是唯一有问题的人。