EXC_BAD_ACCESS 关闭 NSDocument 时 window(启用 ARC)

EXC_BAD_ACCESS when closing NSDocument window (ARC enabled)

我正在努力将基于文档的应用程序从垃圾收集(它 运行 在 10.6 下很好)转换为自动引用计数(试图让它编译并 运行 10.12) .当最后一个 window 关闭时,我得到一个一致的 EXC_BAD_ACCESS。静态分析器未标记任何内容。

我使用 Instruments 寻找 Zombies,确实有 release 消息发送到已释放的对象。这是踪迹:

#   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:]

我在诊断这个问题时遇到的困难是我可以看到消息被发送到僵尸,但是因为我没有释放任何对象(这都是由 ARC 完成的),我猜有某处正在进行的隐式释放。但是我不确定去哪里找。

在调试器中时,lldb 抱怨 main.m 崩溃:

return NSApplicationMain(argc, (const char **) argv);

如有任何帮助,我们将不胜感激。谢谢。

研究了很多,问题还是没有解决,不过我有一个线索:

感谢您的回复。是的,我现在假设这是一个内存管理问题。有办法追查罪魁祸首吗?在 Instruments 中使用 Zombies 似乎没有帮助:它指出了一个问题,但没有帮助我解决问题。也就是说,根据 Instruments,违规行似乎是“-[NSApplication(NSWindowCache) _checkForTerminateAfterLastWindowClosed:saveWindows:]”;但我从未发布过那条线。我在解决问题方面取得了一些进展。

我在文档中使用的结构是:

NSDocumentController:默认,不子类化 NSDocument:子类,MyDocument;也是 window 的代表 NSWindowController:默认,不子类化 NSWindow: MyDocument.xib, MainMenu.xib

我尝试将打开 window 的委托设置为 nil,如下所示:

-(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];
}

这会导致同样的崩溃。

然后,我认为 currentWindowController(或应用程序)可能正在向已释放的 window 发送消息。所以我尝试添加行(在上述方法的末尾):

[currentWindowController setWindow:nil];

这消除了崩溃,但引入了新问题(例如在尝试加载新文件时等)。但我想知道这是否是帮助解决整体问题的线索。

问题是应用委托被设置为 MyDocumentNSDocument subclass)。通过创建标准 AppDelegate class(NSObject,实现 NSApplicationDelegate 协议),在 XIB 中创建相应的 NSObject 并将其设置为输入 AppDelegate,然后让 AppDelegate 成为 MyDocument.

的代表

问题的根源似乎是 MyDocument 对象正在被释放,但它仍然是 NSWindow subclass 的委托。 NSWindow 然后向委托发送了一个 dealloc 消息,但它已经被释放,导致崩溃。

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