Delaying the shutdown sequence

If the computer is in the process of shutting down, you can delay the shutdown if you need to say, put up a dialog box. If your app has an unsaved document, you need to give the user an opportunity to save, not save, or cancel the shutdown outright. There may be other reasons you need to delay the shutdown. For example, I have a simple program which displays a dialog box reminding my wife and son to turn off the wireless mouse when they shutdown. The dialog doesn’t have an OK button so it can’t be dismissed. Instead, I use a timer and a delegate function -(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender.
When the app is told to shutdown, the delegate function is called and you have three return options:
NSTerminateNow – which allows the app to quit immediately and the shutdown to continue
NSTerminateCancel – which cancels the shutdown sequence immediately (You’ll get the user cancelled dialog box)
NSTerminateLater – which delays the shutdown sequence for a bit, giving you time to do something, such as putting up a dialog box. If the delay is too long, the shutdown will eventually be canceled. NSTerminateLater also causes your app to be placed a new runloop mode, NSModalPanelRunLoopMode. Now, if you want to use a timer to dismiss the dialog you’ve put up, such as in my app, you have to take care to place your timer in the NSModalPanelRunLoopMode, otherwise, your timer will never fire. There are 4 class methods for creating an NSTimer:

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds
invocation:(NSInvocation *)invocation repeats:(BOOL)repeats

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds
target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds
invocation:(NSInvocation *)invocation repeats:(BOOL)repeats

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds
target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats

The first two, automatically place the NSTimer in the default runloop, so if you use either of these in this situation, the timer will never fire. Use one of the last two and then you’ll need to manually place the timer in the correct runloop, ala,

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSModalPanelRunLoopMode];

Of course, when you initialize your timer, you need to give it a method to invoke. Within the body of that method, you can just this send this message to NSApp passing YES as the lone parameter:

- (void)replyToApplicationShouldTerminate:(BOOL)shouldTerminate

I have a sample app here. (The crash issue is now fixed)

One other thing to note is the new “Sudden Termination” feature of Snow Leopard (10.6). To speed up shutdown/restart, the OS will immediately terminate an application if it allows for sudden termination. You can read the documentation here in the:

NSProcessInfo docs.

The upshot of this is that if your app should not abruptly quit (in Snow Leopard), you need to disable sudden termination. You can either do this in code ([[NSProcessInfo processInfo] disableSuddenTermination]) or in the app’s plist (NSSupportsSuddenTermination NO). Whatever route you choose, you can re-enable sudden termination in code via [[NSProcessInfo processInfo] enableSuddenTermination].

That’s it.

2 thoughts on “Delaying the shutdown sequence

  1. There is a problem in the sample app.

    The call to [[NSApp sharedApplication] replyToApplicationShouldTerminate:YES]; actually crashes your app with an unrecognized selector (sharedApplication) being sent to the NSApp instance which does not implement that method (it is a class method not an instance method). You just don’t notice that it crashes because you expect it to exit anyways.

    The call should be:
    [[NSApplication sharedApplication] replyToApplicationShouldTerminate:YES];

    Then your application wil shutdown gracefully instead of crashing.

  2. Thank you, good catch. I don’t get a lot of traffic on the blog, and I haven’t checked it in quite some time. WordPress notifications aren’t working either. I’ve put an update out that fixes that. Thanks again.

Leave a Reply

Your email address will not be published. Required fields are marked *