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

Swift关闭窗口触发EXC_BAD_ACCESS问题求助

状态栏菜单窗口二次关闭触发EXC_BAD_ACCESS崩溃问题解决

问题描述

  • 异常表现:首次打开状态栏菜单对应的窗口并关闭无异常,二次打开后关闭触发EXC_BAD_ACCESS (code=1, address=0x20)错误
  • 无效尝试:添加windowWillClose方法将burritoOrderWindow置为nil,设置window.isReleasedWhenClosed=false,虽阻止崩溃但二次打开窗口失败
  • 复现步骤:关闭已打开的窗口后再次打开

原因分析

  1. 默认情况下NSWindow的isReleasedWhenClosed为true,窗口关闭时会被自动释放,但代码中burritoOrderWindow仍持有已释放窗口的强引用,导致悬垂指针
  2. 未将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:31:15