以编程方式创建的 NSWindow 在关闭时崩溃

问题描述 投票:0回答:1

我正在尝试了解 NSWindows 的正确生命周期以及如何正确处理它们。

我有默认的 macOS 应用程序模板,其中一个窗口内有一个 ViewController,该窗口位于一个窗口控制器内。

我还在

applicationDidFinishLaunching
中创建了一个简单的程序窗口,如下所示:

let dummyWindow = CustomWindow(contentRect: .init(origin: .zero, size: .init(width: 200, height: 100)), styleMask: [.titled, .closable, .resizable], backing: .buffered, defer: true)
dummyWindow.title = "Code window"
dummyWindow.makeKeyAndOrderFront(nil)

CustomWindow 类只是:

class CustomWindow: NSWindow {
    deinit {
        print("Deinitializing window...")
    }
}

当我关闭程序窗口时(通过调用

.close()
或仅点击红色关闭按钮,应用程序会因 EXC_BAD_ACCESS 崩溃。即使我没有以任何方式访问该窗口。

有人可能认为这是因为 ARC,但事实并非如此。一——即使

NSApplication.shared.windows
的局部范围结束,窗口仍然被
applicationDidFinishLaunching
强引用。第二,
"Deinitializing window..."
仅在窗口关闭后打印。

关闭 Interface Builder 窗口不会出现任何崩溃。我深入挖掘并玩弄了

isReleasedWhenClosed
属性。对于 IB 窗口来说,无论真假都没有区别。不过,它阻止了程序化窗口的崩溃。

但这提出了三个问题:

  1. 什么是在程序窗口关闭后访问它——导致崩溃,因为 NSWindow 的默认行为是释放它——如果它不是我的代码?
  2. 普通窗口和窗口控制器内防止这些崩溃的窗口之间有什么区别?
  3. 如果程序化窗口的推荐方法是始终设置
    isReleasedWhenClosed = true
    那么您如何实际释放程序化窗口以使其不会无限期地停留在内存中?
swift macos nswindow nswindowcontroller
1个回答
0
投票

1。程序化窗口关闭后访问什么——导致崩溃,因为 NSWindow 的默认行为是释放它——如果它不是我的代码?

崩溃的回溯显示该窗口是从自动释放池引用的。该窗口已被释放。

2。普通窗口和窗口控制器内防止这些崩溃的窗口之间有什么区别?

来自

isReleasedWhenClosed
的文档:

但是,对于窗口控制器拥有的窗口,关闭时释放将被忽略。

3.如果程序化窗口的推荐方法是始终设置 isReleasedWhenClosed = true 那么如何实际释放程序化窗口以使其不会无限期地滞留在内存中?

对于编程窗口,建议的方法是将

isReleasedWhenClosed
设置为
false
,以便窗口遵循 ARC 规则。您可以使用
NSWindowDelegate
方法
windowWillClose(_:)
willCloseNotification
来释放窗口,以防您保留强引用。

AppKit 保留了窗口。我认为这是为了向后兼容。这解释了为什么当

dummyWindow
超出范围并且窗口被释放时窗口不会消失。

如果您的应用程序在 macOS 10.13 SDK 或更高版本上链接,则 AppKit 将强烈引用订购的 NSWindows,直到它们被显式订购或关闭(不包括隐藏或最小化操作)。这意味着,一般情况下,屏幕上的窗口不会被释放(并且关闭/排序是释放的副作用)。

来源:macOS 10.13 的 AppKit 发行说明

© www.soinside.com 2019 - 2024. All rights reserved.