i am working in XCode Swift, with Cocoa with Storyboards, and i need to be able to create and close my own windows, programmatically.
to repeat the problem as simply as possible, i start with an empty storyboard project, and change only viewDidLoad in ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
let rect = NSRect( x:500, y:500, width:800, height:500 )
let mask = NSWindow.StyleMask( arrayLiteral: .closable, .miniaturizable, .resizable, .titled )
let win = NSWindow( contentRect: rect, styleMask: mask, backing: .buffered, defer: false )
win.orderFront( win )
}
the application runs, and puts up two windows. the smaller one is from the storyboard and can be closed without trouble, but when i close the larger window which i created, the application crashes in the appDelegate with
Thread 1: EXC_BAD_ACCESS (code=1, address=0x20)
i assume i am doing something dumb here - what am i missing?
i discovered if i set
win.isReleasedWhenClosed = false
the problem goes away, however i assume the memory is not being released.
i also found that if i append the window handle to an array before closing it, the problem also goes away:
private var broken_windows [NSWindow] = []
public func windowShouldClose( _ sender: NSWindow ) -> Bool {
broken_windows.append( sender )
return true
}
however this array is somehow completely untouchable, and any attempt to access it's members after the window has been closed will cause the application to crash.
CodePudding user response:
update - the following is not correct, setting isReleasedWhenClosed = false
leaves the window open in memory, and is a serious memory leak.
it seems that isReleasedWhenClosed = false
is the correct answer, after all.
the following works for me.
the following solves the problem of the application crashing, but leaves the window behind in memory, the object is not garbage collected.
class ViewController: NSViewController, NSWindowDelegate {
var win: NSWindow? = nil
public func windowShouldClose( _ sender: NSWindow ) -> Bool {
if( sender == win ) { win = nil }
return true
}
override func viewDidLoad() {
super.viewDidLoad()
let rect = NSRect( x:500, y:500, width:800, height:500 )
let mask = NSWindow.StyleMask( arrayLiteral: .closable, .miniaturizable, .resizable, .titled )
win = NSWindow( contentRect: rect, styleMask: mask, backing: .buffered, defer: false )
win!.delegate = self
win!.isReleasedWhenClosed = false
win!.orderFront( win )
DispatchQueue.global(qos: .userInitiated).async {
sleep(5)
print( self.win.debugDescription )
}
}
}
CodePudding user response:
In your updated code, the window is only retained because you are holding a strong reference to it via self.win
. If you use weak var win: NSWindow?
, then self.win
will be nil after the window is closed. So, setting isReleasedWhenClosed = false
does seem to be a valid solution.