I'm working on converting a document-based application from Garbage Collection (it ran fine under 10.6) to Automatic Reference Counting (trying to get it to compile and run for 10.12). I'm getting a consistent EXC_BAD_ACCESS when the last window is closed. Nothing is flagged by the static analyser.
I used Instruments to look for Zombies, and indeed there appears to be a release
message sent to a deallocated object. Here is the trace:
# Event ∆ RefCt Timestamp Responsible Library/Responsible Caller
173 Release -1 3 00:05.226.677 Foundation __48-[NSFileAccessArbiterProxy removeFilePresenter:]_block_invoke
174 Release -1 2 00:05.226.679 Foundation -[NSFilePresenterXPCMessenger invalidate]
175 Retain +1 3 00:05.226.823 Foundation -[NSBlockOperation initWithBlock:]
176 Retain +1 4 00:05.226.858 AppKit -[NSDocument close]
177 Release -1 3 00:05.227.350 Foundation -[NSFilePresenterXPCMessenger dealloc]
Retain/Release (2) 00:05.227.484 AppKit -[NSDocumentController removeDocument:]
180 Release -1 2 00:05.227.485 AppKit -[NSDocumentController removeDocument:]
Retain/Release (2) 00:05.227.496 AppKit -[NSUIActivityManager addProvider:toUserActivity:withSetter:]
183 Autorelease 00:05.227.499 AppKit -[NSWindowController _windowDidClose]
184 Release -1 1 00:05.228.172 Foundation -[NSAutoreleasePool drain]
185 Release -1 0 00:05.228.184 Foundation -[NSBlockOperation dealloc]
186 Zombie -1 00:05.242.579 AppKit -[NSApplication(NSWindowCache) _checkForTerminateAfterLastWindowClosed:saveWindows:]
The difficult I am having in diagnosing this problem is that I can see the message being sent to the Zombie, but because I do not release any objects (it is all done by ARC), I'm guessing there is an implicit release going on somewhere. But I'm not sure where to look.
When in the debugger, lldb
complains of a crash in main.m
at:
return NSApplicationMain(argc, (const char **) argv);
Any help would be greatly appreciated. Thank you.
After doing more research, the problem is still unsolved, but I have a clue:
Thank you for your response. Yes, I am at this point assuming it is a memory management issue. Is there way to track down the culprit? It doesn't seem like using Zombies in Instruments is helping: it points to a problem, but does not help me target the problem. That is, the offending line according to Instruments appears to be "-[NSApplication(NSWindowCache) _checkForTerminateAfterLastWindowClosed:saveWindows:]"; but I never issued that line. I have made what might be a little progress towards figuring things out.
The structure I am using in the document is:
NSDocumentController
: default, not subclassed
NSDocument
: subclassed, MyDocument; also the delegate of window
NSWindowController
: default, not subclassed
NSWindow
: MyDocument.xib
, MainMenu.xib
I tried setting the delegate of the open window to nil as follows:
-(void)windowWillClose:(NSNotification *)notification
{
windowOpen = YES;
NSArray *windowControllers = [self windowControllers];
NSWindowController *currentWindowController = [windowControllers firstObject];
NSWindow *windowForCurrentController = [currentWindowController window];
NSDocument *w = [windowForCurrentController delegate];
[windowForCurrentController setDelegate:nil];
}
This causes the same crash.
Then, I thought perhaps the currentWindowController (or the application) was messaging a deallocated window. So I tried adding the line (at the end of the method above):
[currentWindowController setWindow:nil];
This gets rid of the crashes, but introduces new problems (such as when trying to load new files, etc.). But I'm wondering if this is a clue to help solve the overall problem.
The problem was that the app delegate was set to being MyDocument
(the NSDocument
subclass). The problem was solved by creating a standard AppDelegate
class (NSObject
, implementing the NSApplicationDelegate
protocol), creating a corresponding NSObject
in the XIB and setting it to type AppDelegate
, and then making AppDelegate
the delegate for MyDocument
.
I seems that the source of the problem was that the MyDocument
object was being deallocated, but it was still the delegate of the NSWindow
subclass. The NSWindow
then sent a dealloc message to the delegate, but it was already deallocated, causing a crash.