Swift关闭窗口触发EXC_BAD_ACCESS问题求助
状态栏菜单窗口二次关闭触发EXC_BAD_ACCESS崩溃问题解决
问题描述
- 异常表现:首次打开状态栏菜单对应的窗口并关闭无异常,二次打开后关闭触发
EXC_BAD_ACCESS (code=1, address=0x20)错误 - 无效尝试:添加
windowWillClose方法将burritoOrderWindow置为nil,设置window.isReleasedWhenClosed=false,虽阻止崩溃但二次打开窗口失败 - 复现步骤:关闭已打开的窗口后再次打开
原因分析
- 默认情况下
NSWindow的isReleasedWhenClosed为true,窗口关闭时会被自动释放,但代码中burritoOrderWindow仍持有已释放窗口的强引用,导致悬垂指针 - 未将
AppDelegate设置为窗口的delegate,添加的windowWillClose方法未被触发,无法及时置空引用
解决方案
方案一:正确处理窗口释放与引用置空
通过设置窗口delegate,在窗口关闭时主动置空引用,避免悬垂指针。
完整代码:
import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { var statusBarItem: NSStatusItem! var burritoOrderWindow: NSWindow? func applicationDidFinishLaunching(_ aNotification: Notification) { let statusBar = NSStatusBar.system statusBarItem = statusBar.statusItem(withLength: NSStatusItem.squareLength) statusBarItem.button?.title = "🌯" let statusBarMenu = NSMenu(title: "8gwifi.org") statusBarItem.menu = statusBarMenu statusBarMenu.addItem( withTitle: "Order a burrito", action: #selector(orderABurrito), keyEquivalent: "") statusBarMenu.addItem( withTitle: "Cancel burrito order", action: #selector(cancelBurritoOrder), keyEquivalent: "") } @objc func orderABurrito() { print("Ordering a burrito!") // 复用已存在的窗口 if let window = burritoOrderWindow { window.makeKeyAndOrderFront(nil) return } let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 400, height: 300), styleMask: [.titled, .closable, .resizable], backing: .buffered, defer: false) window.center() window.title = "Burrito Order" window.delegate = self // 设置窗口代理 window.makeKeyAndOrderFront(nil) burritoOrderWindow = window } @objc func cancelBurritoOrder() { print("Canceling your order :(") } // 窗口关闭时置空引用 func windowWillClose(_ notification: Notification) { burritoOrderWindow = nil } }
方案二:复用窗口实例(关闭时隐藏而非释放)
设置窗口关闭时不自动释放,后续打开时直接显示已创建的窗口,避免重复创建释放导致的指针问题。
完整代码:
import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { var statusBarItem: NSStatusItem! var burritoOrderWindow: NSWindow? func applicationDidFinishLaunching(_ aNotification: Notification) { let statusBar = NSStatusBar.system statusBarItem = statusBar.statusItem(withLength: NSStatusItem.squareLength) statusBarItem.button?.title = "🌯" let statusBarMenu = NSMenu(title: "8gwifi.org") statusBarItem.menu = statusBarMenu statusBarMenu.addItem( withTitle: "Order a burrito", action: #selector(orderABurrito), keyEquivalent: "") statusBarMenu.addItem( withTitle: "Cancel burrito order", action: #selector(cancelBurritoOrder), keyEquivalent: "") } @objc func orderABurrito() { print("Ordering a burrito!") // 复用已存在的窗口 if let window = burritoOrderWindow { window.makeKeyAndOrderFront(nil) return } let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 400, height: 300), styleMask: [.titled, .closable, .resizable], backing: .buffered, defer: false) window.center() window.title = "Burrito Order" window.isReleasedWhenClosed = false // 关闭时不释放窗口 window.makeKeyAndOrderFront(nil) burritoOrderWindow = window } @objc func cancelBurritoOrder() { print("Canceling your order :(") } }
方案对比
- 方案一:每次关闭窗口释放实例,内存占用低,适合不频繁打开的场景
- 方案二:复用窗口实例,避免重复创建开销,适合频繁打开关闭的场景
内容的提问来源于stack exchange,提问作者anish
相关产品推荐
相关产品推荐

