Search code examples
objective-cmacosexc-bad-accessnsdocumentnszombie

EXC_BAD_ACCESS when closing NSDocument window (ARC enabled)


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.


Solution

  • 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.

    Here is the chain that solved it (long but instructive).