没有 Storyboard 的 macOS statusBarApp 创建和关闭 settingsWindow 导致 EXC_BAD_ACCESS
macOS statusBarApp without Storyboard create and close settingsWindow causing EXC_BAD_ACCESS
我有一个 MacOS cocoa statusBarApp,没有任何 Storyboard 和 main.swift 文件。
statusBarIcon 显示一个菜单,该菜单显示一个带有按钮的自定义视图,该按钮应该打开一个 settingsWindow - 它确实如此。如果我关闭 settingsWindow 并重新打开它并再次关闭它,我会收到 EXC_BAD_ACCESS 错误。似乎 window 已解除分配,但引用仍然存在。我不知道如何解决这个问题。
按照 Willeke 的建议编辑问题:
感谢您的回答。好的,这是一个最小的可重现示例:
创建一个新的 Xcode 项目,其中包含情节提要和 swift macOS 应用程序。
在 Project-Infos / General / Deployment Info 下:删除故事板的主要条目。然后删除故事板文件本身。
在 Info 下,将“application is agent”标志设置为 yes,因此该应用仅为 statusBarApp。
那么你只需要下面的代码。
异常断点指向这一行:
settingsWindow = NSWindow(
要重现错误:启动应用程序,单击 statusItem,单击 menuItem,打开 window,关闭 window,再次单击所有第一步并重新打开 window.有时这就是崩溃点。有时需要多尝试几次关闭 window,但不要超过 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.
}
}
}
NSWindow
关闭时释放。在 ARC 之前,这是一个有用的功能。可以通过将 isReleasedWhenClosed
属性 设置为 false
来关闭它。但是 window 在关闭时会保留在内存中,因为 settingsWindow
属性 会保留它。实现委托方法 windowWillClose
并将 settingsWindow
设置为 nil
因此 window 被释放。
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
}
}
我有一个 MacOS cocoa statusBarApp,没有任何 Storyboard 和 main.swift 文件。 statusBarIcon 显示一个菜单,该菜单显示一个带有按钮的自定义视图,该按钮应该打开一个 settingsWindow - 它确实如此。如果我关闭 settingsWindow 并重新打开它并再次关闭它,我会收到 EXC_BAD_ACCESS 错误。似乎 window 已解除分配,但引用仍然存在。我不知道如何解决这个问题。
按照 Willeke 的建议编辑问题:
感谢您的回答。好的,这是一个最小的可重现示例:
创建一个新的 Xcode 项目,其中包含情节提要和 swift macOS 应用程序。 在 Project-Infos / General / Deployment Info 下:删除故事板的主要条目。然后删除故事板文件本身。 在 Info 下,将“application is agent”标志设置为 yes,因此该应用仅为 statusBarApp。 那么你只需要下面的代码。
异常断点指向这一行:
settingsWindow = NSWindow(
要重现错误:启动应用程序,单击 statusItem,单击 menuItem,打开 window,关闭 window,再次单击所有第一步并重新打开 window.有时这就是崩溃点。有时需要多尝试几次关闭 window,但不要超过 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.
}
}
}
NSWindow
关闭时释放。在 ARC 之前,这是一个有用的功能。可以通过将 isReleasedWhenClosed
属性 设置为 false
来关闭它。但是 window 在关闭时会保留在内存中,因为 settingsWindow
属性 会保留它。实现委托方法 windowWillClose
并将 settingsWindow
设置为 nil
因此 window 被释放。
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
}
}