macosstate-restorationnswindowrestoration

How to programmatically terminate NSApp without encoding window state?


All OS X application that support NSWindowRestoration can be closed by selecting the menu entry "Quit and Close All Windows" (Option-Command Q). This disables the state restoration and the next time you open the app all windows will be in their default position.

The menu entry triggers the terminate: method on NSApplication. But so does the regular "Close App" menu as well (Command Q).

How can I do the "Quit and Close All Windows" programmatically? Do I really have to close all windows by myself and then call terminate:?

How does Apple magically decide what to do, when both actions are connected to the same terminate: method?


Solution

  • There doesn't seem to be a great way to do this. You might want to file a bug with Apple requesting (along with an explanation for why you need it).

    How does Apple magically decide what to do, when both actions are connected to the same terminate: method?

    Well, looking at the disassembly of AppKit, it appears that -[NSApplication terminate:] checks if the sender is an instance of NSMenuItem. If it is, it checks if its userInterfaceItemIdentifier is equal to @"NSAlternateQuitMenuItem".

    You could, I suppose, create a dummy menu item with that identifier and pass that as the sender to -terminate:, although since this is relying on an implementation detail it could break at any time.

    The other controlling factor is the setting of System Preferences > General > "Close windows when quitting an app". That corresponds to the user defaults key NSAlternateQuitMenuItem, although, again, that's an implementation detail. It appears that you could set that before calling -terminate: and then, in the -applicationWillTerminate: delegate method, remove that setting. (Your changes will be associated with your application. They won't affect other applications or the setting in System Preferences.) Of course, you'll have to make sure that sudden termination is disabled to get that delegate method call.