iOS 中 NSUserDefaults 的默认值初始化问题

iOS 中的 SettingsBundle 可以很方便的实现应用设置界面,你甚至都不用像 Android 中一样去自己写 PreferenceActivity ;然后通过 NSNSUserDefaults 就可以像 Android 中的 SharedPreferences 一样存取信息。

而且,与 Android 只能在读取时设置默认值不同;对于 iOS ,你可以在创建 SettingsBundle 时就预先给每个选项设置默认值。

但是最近在 SettingsBundle 实际使用中发现一个问题:

虽然设置了默认值,但代码中通过 NSUserDefaults 读到的始终为空,必须进入设置页面点一下才会读取到。

自己初步分析,应该是默认值没有被初始化。

StackOverflow 上果然也有人遇到了类似的问题

解决方式是:先从 plist 文件将所有 DefaultValue 读取到一个 NSMutableArray ,然后调用 NSUserDefaultsregisterDefaults: 方法初始化。

这里自己也写了一个初始化方法:

-(void)checkUserDefaultsForKey:(NSString*)prefKey
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    if (![defaults objectForKey:prefKey])
    {
        // Read plist file's path from MainBundle:
        NSString *mainBundlePath = [[NSBundle mainBundle] bundlePath];
        NSString *plistPath = [mainBundlePath stringByAppendingPathComponent:@"Settings.bundle/Root.plist"];

        // Read dictionary from plist file:
        NSDictionary *plistDic = [NSDictionary dictionaryWithContentsOfFile:plistPath];

        // Read Preferences array from dictionary:
        NSMutableArray *prefArray = [plistDic objectForKey:@"PreferenceSpecifiers"];
        NSMutableDictionary *defaultsDic = [NSMutableDictionary dictionary];

        // Travel all properties:
        for(int i = 0; i < [prefArray count]; i++)
        {
            NSString *key = [[prefArray objectAtIndex:i] objectForKey:@"Key"];
            if(key)
            {
				// Write property's DefaultValue to dic:
                id value = [[prefArray objectAtIndex:i] objectForKey:@"DefaultValue"];
                [defaultsDic setObject:value forKey:key];
            }
        }

        // Save DefaultValues:
        [defaults registerDefaults:defaultsDic];
        [defaults synchronize];
    }
}

上面注释还算详尽,就不细说了。

然后,我们在读取 NSUserDefaults 中的值时,如果有设置过默认值,只需要通过其中的一个调用上面的方法就可以了:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[self checkUserDefaultsForKey:@"pref_name"];
NSLog(@"Name: %@", [defaults stringForKey:@"pref_name"]);
// There's no need to check again.
NSLog(@"Level: %d", [defaults integerForKey:@"pref_level"]);

因为上面的 checkUserDefaultsForKey: 方法会遍历所有 DefaultValue ,所以不用再次检验和初始化。