You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 15:20:22