Objective c 为什么';t iPhone应用程序';s main()函数有机会完成吗?

Objective c 为什么';t iPhone应用程序';s main()函数有机会完成吗?,objective-c,iphone,Objective C,Iphone,考虑一下大多数iPhone应用程序使用的main()方法: int main(int argc, char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; int retVal = UIApplicationMain(argc, argv, nil, nil); [pool release]; return retVal; } 在我在模拟器中运行的每个iPhone应用程序

考虑一下大多数iPhone应用程序使用的
main()
方法:

int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}
在我在模拟器中运行的每个iPhone应用程序(包括苹果提供的几个示例项目)中,线程永远不会退出
UIApplicationMain()
,并且
main()
中的任何剩余代码永远不会执行。这是预期的行为吗

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


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

在[pool release]之后,没有什么可以登录的?

尝试使用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");
}
告诉我们会发生什么

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是返回语句的行号

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;
}
可能是池的释放阻止了进一步的日志记录,在这种情况下,您将得到步骤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\u terminateWithStatus://code>

在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()
的秘密。你为什么不试一试呢?即使不能解决核心问题,它也可能使您离目标更近。

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

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

简短回答:因为UIApplicationMain()的编码使其永远不会返回。

在模拟器和设备上做了几次测试,并请另一位开发人员做同样的测试后,我确认UIApplicationMain永远不会返回。当用户按Home按钮正常终止应用程序时,程序最终会在名为_terminateWithStatus的未发布UIApplication方法内终止。此方法调用exit(0)

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

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


感谢大家的投入和头脑风暴我也有过不归路的经历。并设置了断点,以完全按照Clint所说的进行验证

wisequark有一个很好的观点


好话题。让我感到更舒服的是,我不是唯一一个有这个问题的人。

这个理论应该很容易检查,只需在发布前记录即可。我在发布之前使用调试器对其进行了测试,以验证线程从未真正退出UIApplicationMain()函数(即NSLog()从未执行)。可能应该在问题中提到这一点。我应该提到我在调试器中通过main()来验证线程从未退出UIApplicationMain()。也就是说,为了清晰起见,我也进行了你的测试;“步骤2”和“步骤3”不会出现在控制台中。对不起,我应该在问题中提到,我在调试器中通过main()来验证