I have a MacOS cocoa statusBarApp without any Storyboard with main.swift file. The statusBarIcon shows up a Menu which presents a custom view with a button, which should open a settingsWindow - which it does. If I close the settingsWindow and reopen it and close it again, I got a EXC_BAD_ACCESS Error. It seems, that the window is deallocate but the reference is still present. I don't know how to fix this.
Edit the question like Willeke´s advice:
Thx, to your answer. Ok, hier is a minimal reproducible example:
create a new Xcode project, with storyboard and swift for macOS app. Under Project-Infos / General / Deployment Info: Delete the main entry to the storyboard. Then delete the storyboard file itself. Under Info set the "application is agent" flag to yes, so the app is statusBarApp only. then you only need the code below.
The Exception Breakpoint leads to this line:
settingsWindow = NSWindow(
To reproduce the error: start the app, click on statusItem, click on menuItem, a window opens, close the window, click again all first steps and reopen the window. sometimes that's the point of crash. sometimes a few more attempts of closing the window are necessary, but not more then three times.
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.
}
}
}
NSWindow
is released when it is closed. Before ARC this was a usefull feature. It can be switched off by setting the isReleasedWhenClosed
property to false
. But then the window stays in memory when it is closed because the settingsWindow
property is holding on to it. Implement delegate method windowWillClose
and set settingsWindow
to nil
so window is released.
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
}
}