【问题标题】:Removing reference to NSWindowController from AppDelegate on windowWillClose causes crash在 windowWillClose 上从 AppDelegate 中删除对 NSWindowController 的引用会导致崩溃
【发布时间】:2013-08-28 12:13:08
【问题描述】:

我的GameWindowControllerNSWindowController 的子类)中有以下方法:

- (void)windowWillClose:(NSNotification *)notification {
    AppDelegate *delegate = [NSApp delegate];
    [delegate removeGameWindowController:self];
}

AppDelegate 中 removeGameWindowController 的代码为:

- (void)removeGameWindowController:(GameWindowController*)controller {
    [self.controllers removeObject:controller];
}

self.controllers 是一个 NSMutableArray,其中包含我所有的 GameWindowControllers

上面的代码似乎有一个竞争条件。当我关闭窗口时,它会随机崩溃EXC_BAD_ACCESS,如果我一次关闭所有窗口,几乎每次都会崩溃。

我的猜测是 ARC 在 removeGameWindowController: 返回之前或之后释放窗口控制器,使窗口带有一个指向控制器的悬空指针。我尝试添加controller.window.windowController = nil; 无济于事。

出于某种原因,使用(BOOL)windowShouldClose:(id)sender 中建议的委托方法代替https://stackoverflow.com/a/11782844/344544 可行,但不是可接受的解决方案,因为它不会在退出时调用。

如何在每个窗口关闭后可靠地从控制器数组中删除我的窗口控制器?是否有其他一些被调用的委托方法或一些 NSNotification 我可以在窗口完成关闭后订阅哪个火?

【问题讨论】:

    标签: macos cocoa automatic-ref-counting


    【解决方案1】:

    经过长时间的调查并在调试器中逐步运行,我找出了问题的根源和可能的解决方案。

    窗口控制器确实在removeGameWindowController: 结束后的某个时间点连同它的所有强引用一起发布,包括NSWindow。如果在堆栈展开回对窗口本身的close 调用之前释放窗口,则程序将在完成该函数时崩溃,因为self 在这种特殊情况下是一个悬空指针。

    我无法找到在窗口关闭后获得通知的方法,但是这种方法很可能会遇到完全相同的问题。

    为了确保在堆栈中的任何位置都不会留下对窗口的引用,我将窗口控制器从数组中的移除排队等待作为 runloop 上的后续事件发生:

    - (void)removeGameWindowController:(GameWindowController*)controller {
        [self.controllers performSelectorOnMainThread:@selector(removeObject:) withObject:controller waitUntilDone:NO];
    }
    

    【讨论】:

    • 使用performSelector 的变体很少是正确的方法。您是否尝试在调用close 的函数中创建对堆栈上窗口的强引用?这将防止窗口被提前释放。
    • 我无法控制调用 close 的方法,它是由默认菜单栏中的默认关闭菜单栏项调用的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-07
    相关资源
    最近更新 更多