0

我有一个没有任何带有 main.swift 文件的 Storyboard 的 MacOS 可可 statusBarApp。statusBarIcon 显示一个菜单,该菜单显示一个带有按钮的自定义视图,该按钮应该打开一个 settingsWindow - 它确实如此。如果我关闭设置窗口并重新打开它并再次关闭它,我会收到 EXC_BAD_ACCESS 错误。看来,窗口已解除分配,但引用仍然存在。我不知道如何解决这个问题。

像 Willeke 的建议一样编辑问题:

谢谢你的回答。好的,hier 是一个最小的可重现示例:

创建一个新的 Xcode 项目,带有故事板和 Swift 用于 macOS 应用程序。在 Project-Infos / General / Deployment Info 下:删除情节提要的主要条目。然后删除情节提要文件本身。在信息下将“应用程序是代理”标志设置为是,因此该应用程序仅是 statusBarApp。那么你只需要下面的代码。

异常断点导致这一行:

settingsWindow = NSWindow(

要重现错误:启动应用程序,单击 statusItem,单击 menuItem,打开一个窗口,关闭窗口,再次单击所有第一步并重新打开窗口。有时这就是崩溃的关键。有时需要多尝试几次关闭窗口,但不要超过 3 次。

main.swift

import Cocoa

let delegate = AppDelegate()
NSApplication.shared.delegate = delegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

AppDelegate.swift

import Cocoa

class AppDelegate: NSObject, NSApplicationDelegate {
    
    var settingsWindow: NSWindow!
    
    var statusItemMain: NSStatusItem?
    var menuMain = NSMenu()
    var menuItemMain = NSMenuItem()
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application
                
        statusItemMain = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        let itemImage = NSImage(systemSymbolName: "power", accessibilityDescription: nil)
        itemImage?.isTemplate = true
        statusItemMain?.button?.image = itemImage
        
        menuItemMain.target = self
        menuItemMain.isEnabled = true
        menuItemMain.action = #selector(createWindow)
        menuMain.addItem(menuItemMain)
        
        menuMain.addItem(.separator())
                
        statusItemMain?.menu = menuMain
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }

    func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
        return true
    }
    
    @objc func createWindow() {
        settingsWindow = NSWindow(
                    contentRect: NSRect(x: 0, y: 0, width: 750, height: 500),
                    styleMask: [.miniaturizable, .closable, .resizable, .titled],
                    backing: .buffered, defer: false)
        settingsWindow.center()
        settingsWindow.title = "No Storyboard Window"
        settingsWindow.makeKeyAndOrderFront(nil)
        
        settingsWindow?.contentViewController = ViewController()
    }


}

ViewController.swift

import Cocoa

class ViewController: NSViewController {
    
    override func loadView() {
    self.view = NSView(frame: NSRect(x: 0, y: 0, width: 750, height: 500))
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }


}
4

1 回答 1

0

NSWindow关闭时释放。在 ARC 之前,这是一个有用的功能。isReleasedWhenClosed可以通过将属性设置为 来关闭它false。但是,当窗口关闭时,它会保留在内存中,因为settingsWindow属性正在保留它。实现委托方法windowWillClose并设置settingsWindownil释放窗口。

class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {

    var settingsWindow: NSWindow!

    // other methods

    @objc func createWindow() {
        settingsWindow = NSWindow(
                    contentRect: NSRect(x: 0, y: 0, width: 750, height: 500),
                    styleMask: [.miniaturizable, .closable, .resizable, .titled],
                    backing: .buffered, defer: false)
        settingsWindow.isReleasedWhenClosed = false
        settingsWindow.delegate = self
        settingsWindow.center()
        settingsWindow.title = "No Storyboard Window"
        settingsWindow?.contentViewController = ViewController()
        settingsWindow.makeKeyAndOrderFront(nil)
    }
    
    func windowWillClose(_ notification: Notification) {
        settingsWindow = nil
    }

}
于 2022-01-17T17:27:12.803 回答