Programmatically created NSWindow crashes upon close

I am trying to wrap my head around proper lifecycles of NSWindows and how to handle them properly.

I have the default macOS app template with a ViewController inside a window that is inside a Window Controller.

I also create a simple programatic window in applicationDidFinishLaunching like this:

let dummyWindow = CustomWindow(contentRect: .init(origin: .zero, size: .init(width: 200, height: 100)), styleMask: [.titled, .closable, .resizable], backing: .buffered, defer: true)
dummyWindow.title = "Code window"
dummyWindow.makeKeyAndOrderFront(nil)

The CustomWindow class is just:

class CustomWindow: NSWindow {
    deinit {
        print("Deinitializing window...")
    }
}

When I close the programatic window (either by calling .close() or by just tapping the red close button, the app crashes with EXC_BAD_ACCESS. Even though I am not accessing the window in any way.

One might think it's because of ARC but it's not. One—the window is still strongly referenced by NSApplication.shared.windows even when the local scope of applicationDidFinishLaunching ends. And two—the "Deinitializing window..." is only printed after the window is closed.

Closing the Interface Builder window works without any crashes. I dug deep and played with the isReleasedWhenClosed property. It made no difference whether it was false or true for the IB window. It stopped the crashing for the programmatic window though.

But this raises three questions:

  1. What is accessing the programatic window after it's closed—causing a crash because the default behaviour of NSWindow is to release it—if it's not my code?
  2. What is the difference under the hood between a normal window and a window inside a window controller that prevents these crashes?
  3. If the recommended approach for programmatic windows is to always set isReleasedWhenClosed = true then how do you actually release a programatic window so that it does not linger in memory indefinetely?

If the EXC_BAD_ACCESS means that an object is double de-allocated then that would mean that .close() both releases the window (first release) and removes it from the window list which would mean last strong reference is released and ARC cleans the window out (second release).

The theory is supported by me calling .orderOut() instead of close which only removes it from the application list and that does indeed release it without crash. Does this mean programmatic windows should override the close() instance method to call orderOut() instead?

This seems like poor API design or I am understanding it wrong?

Answered by vojta.bohm in 793521022

The correct approach is to set isReleasedWhenClosed = false on every programmatic window so that it follows ARC (Automatic Reference Counting) rules. The reason for this goes way back and a very helpful person on SO has helped me and explained it thoroughly.

Check it out

You declare dummyWindow inside a function. It is destroyed when you leave the appDidFinishLaunching, even though the window remains on display.

Try declaring dummyWindow at the top level.

Accepted Answer

The correct approach is to set isReleasedWhenClosed = false on every programmatic window so that it follows ARC (Automatic Reference Counting) rules. The reason for this goes way back and a very helpful person on SO has helped me and explained it thoroughly.

Check it out

Programmatically created NSWindow crashes upon close
 
 
Q