UPDATE 1: Issue does NOT occur when iOS' data protection (i.e. Passcode Lock) is off!
UPDATE 2: Issue does NOT occur when ARC is disabled.
UPDATE 3: Issue does NOT occur when app is killed prior to restarting iOS.
My Settings class implements the Singleton pattern using +initialize (plus code to see what was going on):
@implementation Settings
static Settings* sharedSettings;
static NSUserDefaults* userDefaults;
+ (void)initialize
{
if ([Settings class] == self)
{
sharedSettings = [self new];
userDefaults = [NSUserDefaults standardUserDefaults];
// Code to see what's going on here ...
if (userDefaults == nil)
{
[[[UIAlertView alloc] initWithTitle:@"userDefaults == nil"
message:nil
delegate:nil
cancelButtonTitle:@"Close"
otherButtonTitles:nil] show];
}
else
{
if ([userDefaults objectForKey:@"Hello"] == nil)
{
[[[UIAlertView alloc] initWithTitle:@"Hello == nil"
message:nil
delegate:nil
cancelButtonTitle:@"Close"
otherButtonTitles:nil] show];
}
else if ([userDefaults boolForKey:@"Hello"] == NO)
{
[[[UIAlertView alloc] initWithTitle:@"Hello == NO"
message:nil
delegate:nil
cancelButtonTitle:@"Close"
otherButtonTitles:nil] show];
}
[userDefaults setBool:YES forKey:@"Hello"];
if ([userDefaults synchronize] == NO)
{
[[[UIAlertView alloc] initWithTitle:@"synchronize == NO"
message:nil
delegate:nil
cancelButtonTitle:@"Close"
otherButtonTitles:nil] show];
}
}
}
}
+ (id)allocWithZone:(NSZone*)zone
{
if (sharedSettings && [Settings class] == self)
{
[NSException raise:NSGenericException format:@"Duplicate Settings singleton creation"];
}
return [super allocWithZone:zone];
}
+ (Settings*)sharedSettings
{
return sharedSettings;
}
@end
I trigger this code in my AppDeletate.m (completely stripped down):
#import "AppDelegate.h"
#import "Settings.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
[Settings sharedSettings];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window makeKeyAndVisible];
return YES;
}
@end
The very weird thing now is that when running this empty app right after restarting iOS, I get both "synchronize == NO" and "Hello == nil" popups. When I then kill the app and run it again, everything is perfectly fine. It turns out that when I delay the userDefaults = [NSUserDefaults standardUserDefaults] and subsequent statements using GCD's dispatch_after(), a little (I did 2 seconds, but much less would probably be equally fine) the problem goes away. Can someone tell me why this is, or is this simple some corner-case iOS bug or side-effect???
ADDITION: It turns out that this issue goes away when I kill the app prior to restarting iOS. This to me proves that it's something in iOS.
Thanks for listening!
Cornelis
ps: You can trust me, I stripped my app completely, there are only three modules: main.mm (the standard XCode generated one-liner), AppDelegate.m (complete code shown above), and Settings.m (complete code shown above). So there is no other code running at all. When I stripped it even further, leaving just AppDelegate.m, the problem remained.