我遇到了关机时自动释放池崩溃的问题,我已经减少到下面的小测试用例,只是创建一个窗口然后关闭它。如果-fobjc-arc
标志被删除,崩溃将消失。在OS X 10.8.2,Clang 4.1(421.11.66)上运行。我希望有更深入了解ARC的人可以告诉我这里发生了什么 - 在节目上运行zombie对象,它是NSWindow对象被释放太多次,或者保留不够,但是我以为ARC是为了照顾这一切吗?
堆栈跟踪是:
0 libobjc.A.dylib 0x00007fff8fad4f5e objc_release + 14
1 libobjc.A.dylib 0x00007fff8fad4230 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 464
2 com.apple.CoreFoundation 0x00007fff99d22342 _CFAutoreleasePoolPop + 34
3 com.apple.Foundation 0x00007fff936e84fa -[NSAutoreleasePool drain] + 154
4 com.apple.Foundation 0x00007fff936effa0 _NSAppleEventManagerGenericHandler + 125
5 com.apple.AE 0x00007fff93a5ab48 aeDispatchAppleEvent(AEDesc const*, AEDesc*, unsigned int, unsigned char*) + 307
6 com.apple.AE 0x00007fff93a5a9a9 dispatchEventAndSendReply(AEDesc const*, AEDesc*) + 37
7 com.apple.AE 0x00007fff93a5a869 aeProcessAppleEvent + 318
8 com.apple.HIToolbox 0x00007fff8d0c18e9 AEProcessAppleEvent + 100
9 com.apple.AppKit 0x00007fff8e95c916 _DPSNextEvent + 1456
10 com.apple.AppKit 0x00007fff8e95bed2 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 128
11 com.apple.AppKit 0x00007fff8e953283 -[NSApplication run] + 517
12 Test 0x00000001070e1d68 main + 152 (Test.mm:31)
13 libdyld.dylib 0x00007fff8e10c7e1 start + 1
测试用例的代码是:
// Tested with `clang++ -fobjc-arc -g Test.mm -framework Cocoa -o Test && ./Test`
#import <Cocoa/Cocoa.h>
@interface MyApplication : NSApplication
@end
@implementation MyApplication
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
NSWindow * window = [[NSWindow alloc] initWithContentRect: NSMakeRect(100, 100, 100, 100)
styleMask: NSTitledWindowMask backing: NSBackingStoreBuffered defer: YES];
[window close];
[super stop: self];
}
@end
int main()
{
@autoreleasepool
{
const ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
SetFrontProcess(&psn);
[MyApplication sharedApplication];
[NSApp setDelegate: NSApp];
[NSApp run];
}
return 0;
}
答案 0 :(得分:8)
使用Instruments的Zombies配置文件显示,通过调用close:
将NSWindow对象放入自动释放池中。一旦applicationDidFinishLaunching:
完成并且销毁NSWindow实例,ARC就会正确地结束引用计数为零。但是,自动释放池仍然知道现在已经解散的NSWindow实例,然后尝试在关闭时释放它,导致崩溃。
自动释放在ARC下管理的对象似乎是一个坏主意,除非自动释放池保持对其对象的弱引用归零,这似乎不是在这里做的。
通过添加[window setReleasedWhenClosed: NO];
告诉窗口不要自动发布,可以防止出现此问题。
答案 1 :(得分:4)
如果将ARC分配给范围大于当前范围的变量,ARC将仅保留新创建的对象。否则,该对象将被泄露。
在您的示例中,您通过调用alloc创建NSWindow
的新实例,该临时将所有权转移到本地变量window
。由于该变量在方法结束时不再存在,因此ARC必须插入release
调用以避免泄漏窗口实例。因此,实例不再由任何东西拥有,因此会自行释放。
要解决此问题,请使用NSWindow
语义声明类型为strong
的属性,并将窗口实例传递给属性setter方法(或直接将其分配给相应的实例变量 - 要么将起作用)。
修改强>
要清楚,您需要做的是将声明的属性(或至少一个实例变量)添加到MyApplication
,例如
@interface MyApplication : NSApplication
@property (strong, nonatomic) NSWindow *window;
@end
然后,在applicationDidFinishLaunching
的实现中,设置属性:
@implementation MyApplication
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(100, 100, 100, 100)
styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:YES];
self.window = window;
...
}
@end
答案 2 :(得分:1)
使用ARC,你需要使用属性来保证长时间漂浮。
要抽象私有属性,请在.m文件中使用匿名类别。
中有更多信息这解决了它:
@interface MyApplication : NSApplication
@property NSWindow *window;
@end
@implementation MyApplication
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
self.window = [[NSWindow alloc] initWithContentRect: NSMakeRect(100, 100, 100, 100)
styleMask: NSTitledWindowMask backing: NSBackingStoreBuffered defer: YES];
[self.window close];
[super stop: self];
}
@end
int main()
{
@autoreleasepool
{
const ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
SetFrontProcess(&psn);
[MyApplication sharedApplication];
[NSApp setDelegate: NSApp];
[NSApp run];
}
return 0;
}
希望有所帮助。