Home > front end >  MacOS Cocoa, why am i not able to close windows?
MacOS Cocoa, why am i not able to close windows?

Time:01-03

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.

  • Related