考虑以下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()
应该如何工作,或者至少在他们的机器上确认相同的行为?
答案 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有一个好点。
很棒的话题。让我感觉更舒服,我不是唯一有问题的人。