第一个样本:
int main(int argc,const char* argv[]) { @autoreleasepool { MyCoolDelegate* myCoolDelegate = [[MyCoolDelegate alloc] init]; [[NSApplication sharedApplication] setDelegate: myCoolDelegate]; return NSApplicationMain(argc,argv); } }
第二个样本:
int main(int argc,const char* argv[]) { @autoreleasepool { [[NSApplication sharedApplication] setDelegate: [[MyCoolDelegate alloc] init]]; return NSApplicationMain(argc,argv); } }
作为C程序员,我希望两个主要函数都应该具有相同的行为,但是返回NSApplicationMain(argc,argv)时会出现第二次主要崩溃;第一个设置委托并按预期工作.
你能解释一下这些样品有什么区别吗? Objective C中的临时对象周围是否有一些黑魔法(我假设[MyCoolDelegate alloc] init]会返回一个MyCoolDelegate类型的临时对象)?
解决方法
实际上,这两个例子在技术上都是错误的.一个“意外工作”,因为编译器没有(当前!)执行合法优化以在最后一次使用时释放myCoolDelegate,这是在NSApplicationMain调用之前.
Per the spec,“默认情况下,自动存储持续时间的局部变量没有精确的生命周期语义.这些对象只是包含可保留对象指针类型值的强引用,并且这些值仍然完全受本地控制下值的优化. “
通常为-setDelegate:
methods don’t retain/strongly-reference things,以防止strong reference cycles,其中两个对象都保持彼此不被释放.为了兼容性,NSApplication使用__unsafe_unretained引用它的委托而不是(通常更可取的)__weak引用(这可能会在将来的OS X版本过程中更改).因此,当NSApplication在解除分配后尝试与其委托进行通信时,会发生崩溃.可以在此处取消分配委托,因为在-setDelegate:call之后没有强烈引用它.
总结:使用隐式临时变量或显式局部变量只保持对象存活,直到变量的最后一次使用.只要变量在范围内,它们就不能保证对象保持活动状态.遗憾的是,编译器并没有像它那样具有攻击性,这使得一个显式的局部变量似乎延长了对象的生命周期,而不是保证它.如果您启用了更多优化,则可能会看到不同的行为.您需要使myCoolDelegate成为一种“更长寿”的变量(ivar,staic或global),或者在声明它时使用objc_precise_lifetime属性.
__attribute__((objc_precise_lifetime)) MyCoolDelegate* myCoolDelegate = [[MyCoolDelegate alloc] init];
这种情况很复杂,但实际上并没有出现太多,因为作为另一个对象的委托的事物几乎总是通过ivar或“长寿”来引用,而不仅仅是局部变量.