应用更新后处理丢失的NSUserDefault密钥

时间:2013-02-23 11:35:39

标签: iphone ios cocoa-touch nsuserdefaults

我刚刚被烧毁,因为我的iOS应用程序的更新在App Store上发布,不幸的是,由于我的代码使用NSUserDefaults方式的错误,它在使用新功能时崩溃了。我的应用使用registerDefaults,但新功能会尝试写入升级后不存在的密钥。如果从头开始安装,它可以正常工作,因为密钥是由registerDefaults创建的。

具体来说,我的应用程序使用嵌套字典,因此在更新之前,首选项文件具有以下结构:

<dict>
    <key>Alpha</key>
    <dict>
        <key>Key</key>
        <string>Value</string>
    </dict>
    <key>Beta</key>
    <dict>
        <key>Key</key>
        <string>Value</string>
    </dict>
    <key>Charlie</key>
    <dict>
        <key>Key</key>
        <string>Value</string>
    </dict>
</dict>

- 更新后,需要键 Delta

<dict>
    <key>Alpha</key>
    <dict>
        <key>Key</key>
        <string>Value</string>
    </dict>
    <key>Beta</key>
    <dict>
        <key>Key</key>
        <string>Value</string>
    </dict>
    <key>Charlie</key>
    <dict>
        <key>Key</key>
        <string>Value</string>
    </dict>
    <key>Delta</key>
    <dict>
        <key>Key</key>
        <string>Value</string>
    </dict>
</dict>

当我的代码尝试从用户默认值中检索 Delta 的字典时,我的代码正在倒下,以便为其写入一些键和值。对于这种情况,字典为nil,因为更新后它不在首选项文件中。

处理这种情况的最佳做法是什么?我是否应该在尝试写入 Delta 之前检查 Delta 的字典是否为nil,并在需要时创建它,或者是否有更少的腰带和括号方法?

2 个答案:

答案 0 :(得分:1)

我遵循以避免这些情况的模式如下:

在我的ApplicationDelegate +(void)initialize方法中,我设置了密钥及其默认值,以确保在设置之前没有尝试触摸默认值:

+(void)initialize
{
    NSDictionary *factoryPrefs = @{MyNewPrefKey:@"ANewPrefKeyDefaultValue"};
}
[[NSUserDefaults standardUserDefaults] registerDefaults:factoryPrefs];
userSettings = [NSUserDefaults standardUserDefaults];

接下来,在此之后,我有一个基于当前版本的代码块,开始检查是否存在暗示用户正在升级(不是新的)的先前值。在那种情况下,我将不得不迁移/更新prefs。

例如,有时必须更改键或插入值,就像在某些先前版本中存储了值为a,b,c的NSDictionary对象一样,但现在在此版本中。你依赖(并假设)'d'值也在那里。

在这种情况下,registerDefaults无法提供帮助,因为您注意到之前创建的值。所以你必须手动检查'd'是否在那里,如果不插入它与默认值。这是Prefs的CoreData迁移等价物:)

这就是我的工作。我是虔诚地做的。我检查它每次发布,因为...我被烧了,就像你以前做的那样。然后我们都知道接下来会发生什么,你会得到一个认真的1星...在App Store中呐喊。

答案 1 :(得分:1)

我对您的数据做了一个简单的测试:

  1. 将defaults.plist文件添加到空项目中;

  2. 将defaults.plist设置为包含Alpha / Beta / Charlie键(根据您的示例);

  3. 在appDidFinishLaunching时使用以下代码:

    NSString* path = [[NSBundle mainBundle] pathForResource:@"defaults" ofType:@"plist"];
    NSDictionary* factoryPrefs = [NSMutableDictionary dictionaryWithContentsOfFile:path];
    [[NSUserDefaults standardUserDefaults] registerDefaults:factoryPrefs];
    NSLog(@"Delta Key: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"Delta"]);
    
    //-- store some user custom values
    NSLog(@"Charlie Key %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"Charlie"]);
    [[NSUserDefaults standardUserDefaults] setObject:@"test" forKey:@"Charlie"];
    NSLog(@"Charlie Key %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"Charlie"]);
    
  4. 运行应用程序;

  5. 结果是:

    2013-02-23 17:59:10.795 JigSaw[14747:c07] Delta Key (null)
    2013-02-23 17:59:10.797 JigSaw[14747:c07] Charlie Key {
        Key = Value;
    }
    2013-02-23 17:59:10.797 JigSaw[14747:c07] Charlie Key test
    

    然后,我通过添加Delta键(再次,根据您的示例)更改了defaults.plist的内容,并再次构建/运行应用程序(当然,不从设备中删除它)。

    结果是:

    2013-02-23 18:00:37.840 JigSaw[15040:c07] Delta Key {
        Key = Value;
    }
    2013-02-23 18:00:37.842 JigSaw[15040:c07] Charlie Key test
    2013-02-23 18:00:37.842 JigSaw[15040:c07] Charlie Key test
    

    因此,registerDefaults似乎会在使用时正确处理任何新添加的密钥,如上例所示。

    我倾向于认为您错误地使用registerDefaults,或者您正在其他地方执行导致崩溃的原因。

    在更一般的层面上,除了测试应用程序更新方案(事后很容易建议,而且我们都以某种方式学习它),对我来说,良好的做法是我确保即使存在错误或无法预料的输入,您的应用也不会崩溃。

相关问题