问题描述
在开发macOS应用时,通常需要使用NSWindow创建各种弹窗或普通窗口。例如:
var TipsAccessibilityWindow: NSWindow? = nil
这是一个可选的提示辅助功能的Window窗口。
当窗口需要关闭时,将该Window窗口设置为nil时,会发生崩溃的问题:
Button(action: {
// 隐藏窗口
WindowManager.shared.TipsAccessibilityWindow = nil
},label: {
Text("取消")
})

Xcode报错信息为:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x186aa23c4)
这里的问题:当使用Button设置TipsAccessibilityWindow为nil时,在主线程的操作发现崩溃。
问题原因
当我将TipsAccessibilityWindow设置为nil时,只是断开了窗口的引用,窗口本身仍然存在(没有被关闭、没有被销毁),仍然在屏幕上,系统持有TipsAccessibilityWindow的强引用。
窗口的delegate和各种操作可能会访问TipsAccessibilityWindow,就导致访问已销毁的对象,从而发生崩溃。
macOS 中,窗口的生命周期受运行循环、显示系统、NSApplication 事件系统共同管理。
当设为 nil,只是断开了类中属性的引用,而:
1、系统仍然保有窗口对象;
2、它没有调用 window.close(),没有退出显示栈;
3、它的 delegate 还在指向某个对象;
4、它的事件循环还在运行(点击、关闭按钮、resize 等);
所以:窗口没有关闭,引用没有完全释放,一旦再被系统事件访问,指针就错乱了。
解决方案
在SwiftUI中,使用close()方法,关闭窗口:
Button(action: {
WindowManager.shared.TipsAccessibilityWindow?.close()
// 可选:这里不需要再手动 = nil,close 后自动释放(除非你自己保留了强引用)
}, label: {
Text("取消")
})
关闭后,窗口退出。如果使用NSWindowDelegate代理的话,可以通过windowWillClose方法进行验证:
class WindowManager:NSWindowController,NSWindowDelegate {
static let shared = WindowManager()
var TipsAccessibilityWindow: NSWindow? = nil
func windowWillClose(_ notification: Notification) {
if let window = notification.object as? NSWindow,window == TipsAccessibilityWindow {
print("当前 TipsAccessibilityWindow 窗口被关闭,清理辅助窗口")
}
}
}
当关闭调用close()方法,关闭窗口时,Xcode会输出:
当前 TipsAccessibilityWindow 窗口被关闭,清理辅助窗口
延伸问题
当我们使用close() 方法关闭窗口后,将TipsAccessibilityWindow变量改为nil,这时仍然会触发崩溃问题:
Button(action: {
WindowManager.shared.TipsAccessibilityWindow?.close()
WindowManager.shared.TipsAccessibilityWindow = nil // 设置为nil
},label: {
Text("Cancel")
})

这也就意味着,我们在创建TipsAccessibilityWindow窗口后,无法设置为nil,断开引用,即使已经调用close()方法关闭窗口。
当我们添加输出时,也可以验证close()方法关闭窗口在前,TipsAccessibilityWindow设置为nil在后:
Button(action: {
WindowManager.shared.TipsAccessibilityWindow?.close()
print("完成窗口的关闭")
WindowManager.shared.TipsAccessibilityWindow = nil // 设置为nil
print("结束Button")
},label: {
Text("Cancel")
})
class WindowManager:NSWindowController,NSWindowDelegate {
static let shared = WindowManager()
var TipsAccessibilityWindow: NSWindow? = nil
func windowWillClose(_ notification: Notification) {
if let window = notification.object as? NSWindow,window == TipsAccessibilityWindow {
print("当前 TipsAccessibilityWindow 窗口被关闭,清理辅助窗口")
}
}
}
Xcode输出:
当前 TipsAccessibilityWindow 窗口被关闭,清理辅助窗口
完成窗口的关闭
结束Button
总结
在macOS中,如果想要关闭NSWindow,需要调用NSWindow的close()方法关闭窗口,不能将NSWindow对应的变量改为nil,因为这个设置nil的操作会引发崩溃的问题。
这个崩溃的问题在于,访问已释放的内存(悬空引用)导致的崩溃。
在macOS的窗口系统中,调用 .close() 方法关闭窗口后,如果访问NSWindow对应的变量,变量引用的窗口很可能被系统销毁了部分资源,但Swift仍然保留着dangling pointer(悬空指针)。
即使NSWindow对应的变量是可选的,如果它被某处代码强引用,那么即使设置为nil,背后仍然有引用存在。
因此,正确的做法就是调用close()方法关闭窗口,不把NSWindow对应的变量设置为nil,而是交给生命周期自动释放。
相关文章
1、Swift悬空引用:https://fangjunyu.com/2025/07/28/swift%e6%82%ac%e7%a9%ba%e5%bc%95%e7%94%a8/