I have an app that uses [NSUserDefaults standardUserDefaults] to store session information. Generally, this information is checked on app launch, and updated on app exit. I
iOS 8 introduced a number of behavior changes to NSUserDefaults
. While the NSUserDefaults
API has changed little, the behavior has changed in ways that may be relevant to your application. For example, using -synchronize
is discouraged (and always has been). Addition changes to other parts of Foundation and CoreFoundation such as File Coordination and changes related to shared containers may affect your application and your use of NSUserDefaults
.
Writing to NSUserDefaults
in particular has changed because of this. Writing takes longer, and there may be other processes competing for access to the application's user defaults storage. If you are attempting to write to NSUserDefaults
as your application is exiting, your application may be terminated before the write is committed under some scenarios. Forcefully terminating using exit(0)
in your example is very likely to stimulate this behavior. Normally when an application is exited the system can perform cleanup and wait for outstanding file operations to complete - when you are terminating the application using exit()
or the debugger this may not happen.
In general NSUserDefaults
is reliable when used correctly on iOS 8.
These changes are described in the Foundation release notes for OS X 10.10 (currently there is not a separate Foundation release note for iOS 8).
As others had pointed out using exit() and generaly exiting your app yourself is really bad idea in iOS.
But I probably know what do you have to deal with. We did develop an enterprise application as well and even though we tried to convince the client that in iOS it is against all the rules and best practices, they insisted that we close the app at one point.
Instead of exit() we used this piece of code:
UIApplication *app = [UIApplication sharedApplication];
[app performSelector:@selector(suspend)];
As the name suggests it only suspends the app as if the user pressed home button. Therefore your saving methods might be able to finish correctly.
I haven't tested this solution for your particular case though, and I'm not sure if suspend is enough for you, but for us it worked.
I have solved similar issues by making changes to NSUserDefaults only in the main thread.
As gnasher729 said, don’t call exit(). There may be an issue with NSUserDefaults in iOS8, but calling exit() simply won’t work.
You should see David Smith’s comments on NSUserDefaults (https://gist.github.com/anonymous/8950927):
Terminating an app abnormally (memory pressure kill, crash, stop in Xcode) is like git reset --hard HEAD, and leaving
Since this is an Enterprise App and not an App Store app, you can try:
@interface UIApplication()
-(void) _terminateWithStatus:(int)status;
@end
and then call:
[UIApplication.sharedApplication _terminateWithStatus:0];
It's using an undocumented API so may not work in previous or future versions of iOS.
I found NSUserDefaults to behave nicely on iOS 8.4 when using a suite name to create an instance instead of relying on standardUserDefaults
.
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"MySuiteName"];