【问题标题】:Why doesn't an iPhone app's main() function ever get a chance to finish?为什么 iPhone 应用程序的 main() 函数没有机会完成?
【发布时间】:2009-02-18 01:25:54
【问题描述】:

考虑以下main() 方法,您会发现大多数 iPhone 应用程序:

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

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

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

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

【问题讨论】:

    标签: objective-c iphone


    【解决方案1】:

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

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

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

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

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

    感谢大家的所有投入和集思广益!

    【讨论】:

    • 更新:我很高兴地报告 Apple 修改了 UIApplicationMain() 的官方文档并关闭了我的错误。该文档现在包括以下内容:即使指定了整数返回类型,此函数也永远不会返回。当用户通过按下 Home 按钮终止 iPhone 应用程序时,应用程序会立即通过调用带有零参数的退出系统函数退出。"
    【解决方案2】:

    试试:

    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、...) - 最终,当日志消息将停止记录,该行将成为您的罪魁祸首(“关闭”日志记录或退出而不正常返回)。

    我在网上找到的另一个 sn-p:

    =====

    当你使用这条线时:

    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 开发可能会有所不同。

    不过,这是一个调查的途径。如果您提供了一个,我很想看看对代表进行了哪些调用。上面的 sn-p 包含的代码是这样的:

    @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() 的秘诀。你为什么不试一试?即使它不能解决核心问题,它也可能会让你更接近目标。

    【讨论】:

    • 我应该提到我在调试器中单步执行 main() 以验证线程永远不会退出 UIApplicationMain()。也就是说,为了清楚起见,我还进行了您的测试; “第 2 步”和“第 3 步”未出现在控制台中。
    【解决方案3】:

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

    【讨论】:

    • 那个理论应该很容易检查,在发布前就登录。
    • 我在发布之前使用调试器对其进行了测试,以验证线程永远不会真正退出 UIApplicationMain() 函数(即 NSLog() 永远不会执行)。可能应该在问题中提到这一点。
    【解决方案4】:

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

    【讨论】:

    • 对不起,我应该在问题中提到我在调试器中单步执行 main() 以验证线程永远不会退出 UIApplicationMain()。确实,当您通过单击“主页”按钮停止 iPhone 应用程序时,预期的行为是在 main() 退出之前终止线程。
    • 嗯,这只是证实了 [pool release] 防止登录错误的理论。
    【解决方案5】:

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

    【讨论】:

    • 同意。然而,我担心的不是池被释放,而是我自己的应用程序做错了什么并且没有正确终止(基于 Apple 的文档, main() 应该完成)。但是你有一个关于关闭/清理效率和避免 dealloc 调用的好点。
    【解决方案6】:

    我也有过这种不回的经历。并设置了断点来验证,就像克林特说的那样。

    wisequark 有一个很好的观点。

    很棒的话题。让我感到更自在,我不是唯一一个有问题的人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-22
      • 2020-11-15
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多