I am having an issue with an autorelease pool crash on shutdown which I've reduced to the small test case below that simply creates a window and then closes it. The crash disappears if the -fobjc-arc
flag is taken away. Running on OS X 10.8.2, Clang 4.1 (421.11.66). I am hoping that someone with a more in depth understanding of ARC can enlighten me as to what is going on here - running with zombie objects on shows that it is the NSWindow object that is getting released too many times, or not retained enough, but I thought ARC was meant to take care of all this?
The stack trace is:
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
And the code for the test case is:
// 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;
}
ARC will only retain a newly created object if you assign it to a variable that has an extent greater than the current scope. Otherwise, the object would be leaked.
In your example, you're creating a new instance of NSWindow
by calling alloc, which temporarily transfers ownership to the local variable, window
. Since that variable ceases to exist at the end of method, ARC has to insert a release
call to avoid leaking the window instance. As a result, the instance is no longer owned by anything, and therefore deallocates itself.
To fix this, declare a property of type NSWindow
with strong
semantics, and pass the window instance to the property setter method (or directly assign it to the corresponding instance variable -- either will work).
EDIT
To be clear, what you need to do is add a declared property (or at least an instance variable) to MyApplication
, for example
@interface MyApplication : NSApplication
@property (strong, nonatomic) NSWindow *window;
@end
Then, in your implementation of applicationDidFinishLaunching
, set the property:
@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
Using Instruments' Zombies profile showed that the NSWindow object gets put into the autorelease pool by the call to close:
. ARC then correctly ends up with a reference count of zero once applicationDidFinishLaunching:
completes and destroys the NSWindow instance. However, the autorelease pool still knows about the now-defunct NSWindow instance and then tries to release it on shutdown, causing the crash.
Autoreleasing objects being managed under ARC seems like a bad idea unless the autorelease pool holds zeroing weak references to its objects, which it doesn't seem to be doing here.
The problem can be prevented by telling the window not to autorelease on close by adding [window setReleasedWhenClosed: NO];
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With