macOS Cocoa中关闭About窗口触发EXC_BAD_ACCESS错误求助
问题分析与解决:关闭About窗口触发EXC_BAD_ACCESS错误
错误原因
- 核心问题出在Close菜单项的action目标配置:你设置的
#selector(NSWindow.performClose(_:))未指定正确的目标对象,当按下Command+W或点击关闭按钮时,系统尝试向当前第一响应者发送performClose(_:)消息,但About窗口未与该菜单项正确关联,导致内存访问越界。 - 额外的潜在问题:
openInWindow方法创建的About窗口没有被持有,窗口关闭时可能出现内存释放异常,但直接触发错误的是菜单action的目标不明确。
解决步骤
1. 修正Close菜单项的目标
将Close菜单项的target设为nil,让Cocoa自动沿响应链寻找合适的处理对象(当前活跃窗口)。修改buildMainMenu中的mainMenuItem1代码:
let mainMenuItem1 = NSMenuItem(title: "Close", action: #selector(NSWindow.performClose(_:)), keyEquivalent: "w") mainMenuItem1.keyEquivalentModifierMask = .command mainMenuItem1.target = nil // 关键修改:让系统自动匹配响应对象
2. 持有About窗口实例(可选但推荐)
在AppDelegate中添加属性持有About窗口,避免窗口被提前释放:
class AppDelegate: NSObject, NSApplicationDelegate { private var window: NSWindow! private var aboutWindow: NSWindow? // 添加该属性 // ... 其他原有代码 } // 同时修改NSApplication的about方法,将创建的窗口赋值给持有属性 extension NSApplication { @objc func about() { guard let appDelegate = NSApp.delegate as? AppDelegate else { return } appDelegate.aboutWindow = AboutView().openInWindow(title: "About My App", sender: self) } }
修改后的完整代码
import Cocoa import SwiftUI class AppDelegate: NSObject, NSApplicationDelegate { private var window: NSWindow! private var aboutWindow: NSWindow? func applicationDidFinishLaunching(_ aNotification: Notification) { buildMainMenu() buildWindow() } func buildWindow() { window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 100.0, height: 100.0), styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], backing: .buffered, defer: false) window.center() window.setFrameAutosaveName("Main Window") window.title = "No Storyboard Window" window.makeKeyAndOrderFront(window) } func buildMainMenu() { let appMainMenu: NSMenu = NSMenu() let mainMenu: NSMenuItem = NSMenuItem() mainMenu.submenu = NSMenu(title: "MainMenu") let mainMenuItem0 = NSMenuItem(title: "About", action: #selector(NSApplication.about), keyEquivalent: "a") mainMenuItem0.keyEquivalentModifierMask = .command let mainMenuItem1 = NSMenuItem(title: "Close", action: #selector(NSWindow.performClose(_:)), keyEquivalent: "w") mainMenuItem1.keyEquivalentModifierMask = .command mainMenuItem1.target = nil // 关键修改 let mainMenuItem2 = NSMenuItem(title: "Quit", action: #selector(NSApplication.shared.terminate(_:)), keyEquivalent: "q") mainMenuItem2.keyEquivalentModifierMask = .command mainMenu.submenu?.items = [mainMenuItem0, mainMenuItem1, mainMenuItem2] appMainMenu.items = [mainMenu] NSApp.mainMenu = appMainMenu } } extension NSApplication { @objc func about() { guard let appDelegate = NSApp.delegate as? AppDelegate else { return } appDelegate.aboutWindow = AboutView().openInWindow(title: "About My App", sender: self) } } struct AboutView: View { var body: some View { Color.green .frame(width: 500, height: 200) } } extension View { @discardableResult func openInWindow(title: String, sender: Any?) -> NSWindow { let window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], backing: .buffered, defer: false) window.center() window.contentView = NSHostingView(rootView: self) window.title = title window.makeKeyAndOrderFront(sender) return window } }
关键说明
- 当菜单项
target设为nil时,Cocoa会自动遍历响应链,找到当前活跃的NSWindow来处理performClose(_:),避免了目标不明确导致的内存访问错误。 - 持有About窗口实例可以防止窗口在关闭流程中被意外释放,进一步提升内存稳定性。
内容的提问来源于stack exchange,提问作者swiftPunk
相关产品推荐
相关产品推荐

