简短版本:
release
当Cocoa应用程序终止时,为什么NSView对象的子视图没有发送消息?
有没有办法覆盖这种行为?
示例:
下面MyView
显示的类只不过NSView
是在创建和销毁时向控制台报告的子类.我测试了它,发现它正常工作.但是,当我使用它时,如我的应用程序委托的下一个代码片段所示,我看到一些意外的事情(请参阅示例输出).
// MyView: @interface MyView : NSView { } @end @implementation MyView - (id)initWithFrame:(NSRect)frameRect { if ((self = [super initWithFrame:frameRect]) == nil) { return nil; } NSLog(@"init %@", self); return self; } - (void)dealloc { NSLog(@"dealloc %@", self); [super dealloc]; } @end
// Application delegate: - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { NSLog(@"begin"); parentView = [[MyView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; MyView * myView = [[MyView alloc] initWithFrame:NSMakeRect(10, 10, 80, 80)]; [parentView addSubview:myView]; [myView release]; NSLog(@"run"); } - (void)applicationWillTerminate:(NSNotification *)aNotification { NSLog(@"quit"); [parentView release]; NSLog(@"end."); }
This application produces the following output:
begin
init<
MyView:0x10013f840>
初始化<
MyView:0x10261b620>
运行
退出
dealloc<
MyView:0x10013f840>
结束.
问题:
我可以清楚地看到第一个视图对象在应用程序退出时被释放,并且我确信(测试和验证)NSView
对象在它们自己被释放时会自动释放它们的子视图.但是,似乎在应用程序终止期间,这些子视图未被释放.
长版本:( AKA为什么人们会关心?)
让我首先说一下,我熟悉一个正在运行的应用程序退出时释放内存的方式.我知道我的子视图将被妥善处理,即使他们从未发送过release
消息,所以我并不担心这是泄漏.事实上,我很确定(但不是100%肯定)我的问题#1的答案是:"因为当应用程序即将终止时,不需要释放子视图."
我的应用程序在调试模式下运行时,我使用一些简单的手动代码来进行内存跟踪.我调用了所有自定义类的and Trace_Init()
和Trace_Dealloc()
方法中的a 和方法,并且在应用程序的Cocoa部分完成后,我使用该函数报告任何未发布的对象.我发现这比定期运行Apple的内存泄漏性能工具简单得多.如果我在运行时导致内存泄漏,我会在应用程序退出时立即知道.init
dealloc
atexit()
但是,dealloc
在终止期间缺少调用意味着NSView
当我退出应用程序时,用作子视图的任何自定义子类都显示为内存泄漏.因此我的问题#2的原因.我希望Cocoa在终止期间释放所有内容,以便我的内存跟踪能够正确包装.当然,我只会在调试模式下覆盖默认行为.我发布的应用程序没有启用内存跟踪代码,应该能够像平常一样有效地退出.
而已!(phew)如果你做到这一点,谢谢你花时间阅读全部内容.
我想到了.解决方案是在方法中创建和释放我自己NSAutoreleasePool
的applicationWillTerminate:
.
详细说明:
在肠子深NSView
的dealloc
方法,各种事情都做了,除去该视图及其所有子视图从响应链,建立了一个关键的看法,代表发送邮件,等等.一些在此代码,每个子视图发送一条retain
消息,后来发送了autorelease
一条消息.(实际上,每个子视图都会被保留并自动释放两次 - 请参阅下面的详细信息).这是正常的,但这里是踢球者:当子视图被发送一条autorelease
消息时,它们会被添加到NSAutoreleasePool
那个时刻发生的任何活动中,并且它们会被保留,直到该特定的池超出范围.在应用程序终止的情况下,它们添加到的池是在应用程序的主事件循环的每次迭代期间自动创建的池 ,并且该池永远不会发送release
消息,因为应用程序即将退出!
实验结果:
我添加了一堆记录消息到的init
,retain
,release
,和autorelease
方法MyView
,它们都具有类似的代码如下:
NSLog(@"[%@ retain]: count = %d", [self name], [self retainCount]+1); return [super retain];
我还记录{
}
了代码,dealloc
以便我可以看到魔法何时发生.
使用这些日志消息,这是我的NSView
obejcts 发生的事情:
begin [parent init]: count = 1 [subview init]: count = 1 [subview retain]: count = 2 [subview release]: count = 1 run quit [parent release]: count = 0 [parent dealloc] { [subview retain]: count = 2 [subview autorelease]: count = 2 [subview retain]: count = 3 [subview autorelease]: count = 3 [subview release]: count = 2 } end.
现在,当我使用以下代码时 applicationWillTerminate:
- (void)applicationWillTerminate:(NSNotification *)aNotification { NSLog(@"quit"); NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; [parentView release]; [pool release]; NSLog(@"end."); }
结果如下:
begin [parent init]: count = 1 [subview init]: count = 1 [subview retain]: count = 2 [subview release]: count = 1 run quit [parent release]: count = 0 [parent dealloc] { [subview retain]: count = 2 [subview autorelease]: count = 2 [subview retain]: count = 3 [subview autorelease]: count = 3 [subview release]: count = 2 } [subview release]: count = 1 [subview release]: count = 0 [subview dealloc] { } end.
您可以清楚地看到release
发送到子视图的两条消息NSAutoreleasePool
.
参考资料:来自Apple开发者文档的GNUStep
Autorelease
Pools中的NSView.m