macOS NSWindow缺失NSViewController导致关闭窗口崩溃问题
macOS NSWindow缺失NSViewController导致关闭窗口崩溃问题

macOS NSWindow缺失NSViewController导致关闭窗口崩溃问题

问题描述

在创建NSWindow的过程中,使用NSWindow.contentView设置视图:

let hostingView = NSHostingView(rootView:
    image
    .resizable()
    .scaledToFit()
    .frame(minWidth: 400, minHeight: 400)
    .padding()
)

let window = NSWindow()
window.contentView = hostingView
window.title = "Image Preview"
window.setContentSize(NSSize(width: 600, height: 600))
window.styleMask = [.titled, .closable, .resizable]
window.center()
window.makeKeyAndOrderFront(nil)
self.window = window

当我尝试关闭窗口时,发现NSWindow创建的窗口发生崩溃。

问题复现

在复现问题时,发现当创建第一个NSWindow时,关闭窗口不会报错。

当创建第二个NSWindow或者关闭第二个NSWindow时,Xcode会立即崩溃并弹出报错信息:

Thread 1: EXC_BAD_ACCESS (code=1, address=0x20)

这个问题在于,我把 NSHostingView 直接赋给了 NSWindow.contentView,但是没有托管在一个 NSViewController 中,导致窗口关闭时内存释放顺序错误(野指针),从而崩溃。

解决方案:使用NSWindow(contentViewController:)初始化方法,而是而不是直接初始化NSWindow:

let hostingViewController = NSHostingController(rootView:
    image
    .resizable()
    .scaledToFit()
    .frame(minWidth: 400, minHeight: 400)
    .padding()
)
        
let window = NSWindow(contentViewController: hostingViewController)

这一问题在于,我使用NSHostingController将SwiftUI桥接到AppKit中使用。

当我只设置NSWindow.contentView为NSView后,窗口不知道谁是“视图控制器”,导致窗口生命周期无法正常结束,因此第二次打开/关闭图片时,就会发生崩溃。

当使用contentViewController绑定NSViewController后,窗口关闭后会自动清理视图,因此不会发生崩溃。

总结

在AppKit中,一个NSWindow管理的是一个完整的UI层级。

NSWindow只负责“容器”,所以需要一个控制器来管理内容。

NSViewController是逻辑与生命周期的管理者,NSView是具体内容。

当跳过NSViewController直接在NSWindow中设置内容视图是:

这时NSWindow不知道谁在管理视图。

当窗口关闭时,窗口实际上并没有立即释放只是隐藏了。所以self.window还持有window的强引用,于是旧的 NSHostingView 的 SwiftUI runtime 仍然在运行 —— 但它的 window 指针已经被系统释放(AppKit 清理了窗口资源)。

因为窗口并没有销毁,其内部的 SwiftUI 生命周期也无法正确释放,这本质上是 AppKit 与 SwiftUI 生命周期模型不兼容所致。

最终导致关闭窗口时,SwiftUI 内部残留的视图树引用访问了已经释放的对象(如 layout 引擎、host window、环境值等),从而导致:

EXC_BAD_ACCESS (code=1, address=0x20)   // 访问了已经释放的地址(野指针)。

当使用NSViewController管理时,它托管了 SwiftUI 的生命周期。

当窗口关闭,系统会自动调用 viewWillDisappear → deinit。

所有 SwiftUI 状态、绑定、视图树都会 安全、明确、同步地销毁。

不会再有悬空引用。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注