Iphone 在应用程序更新后处理缺少的NSUserDefault密钥

Iphone 在应用程序更新后处理缺少的NSUserDefault密钥,iphone,ios,cocoa-touch,nsuserdefaults,Iphone,Ios,Cocoa Touch,Nsuserdefaults,我刚刚被烧死,因为我的iOS应用程序的更新在应用程序商店上线,不幸的是,它在使用新功能时崩溃,因为我的代码使用NSUserDefaults的方式有一个bug。我的应用程序使用了registerDefaults,但新功能试图写入升级后不存在的密钥。如果从头开始安装,它可以正常工作,因为密钥是由registerDefaults创建的 具体来说,我的应用程序使用嵌套字典,因此在更新首选项文件之前,首选项文件具有以下结构: <dict> <key>Alpha</ke

我刚刚被烧死,因为我的iOS应用程序的更新在应用程序商店上线,不幸的是,它在使用新功能时崩溃,因为我的代码使用
NSUserDefaults
的方式有一个bug。我的应用程序使用了
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

在我的applicationelegate
+(void)initialize
方法中,我设置了按键及其默认值,以确保在设置之前不会有任何人试图触摸默认值:

+(void)initialize
{
    NSDictionary *factoryPrefs = @{MyNewPrefKey:@"ANewPrefKeyDefaultValue"};
}
[[NSUserDefaults standardUserDefaults] registerDefaults:factoryPrefs];
userSettings = [NSUserDefaults standardUserDefaults];
接下来,紧接着,我有一段基于当前版本的代码,开始检查是否存在以前的值,这些值表示用户正在升级(不是新的)。在这种情况下,我将不得不迁移/更新prefs

例如,有时必须更改键或插入值,就像您在以前的某个版本中存储了一个NSDictionary对象,其中包含值a、b、c,但现在在这个版本中一样。您依赖(并假设)的“d”值也在其中

在这种情况下,
registerDefaults
不会有帮助,因为正如您所指出的,该值是以前创建的。因此,您必须手动检查“d”是否在其中,如果没有,则将其与默认值一起插入。这是一种相当于Prefs的CoreData迁移:)


我就是这么做的。我虔诚地做这件事。我每次发布都会检查,因为。。。我被烧伤了,就像你以前不做的那样。然后我们都知道接下来会发生什么,你会得到一个严肃的a。。在App Store中大声叫喊。

我用你的数据做了一个简单的测试:

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

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

  • 在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"]);
    
  • 运行应用程序

  • 结果是:

    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
    
    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
    
    然后,我通过添加Delta键(同样,根据您的示例)更改了defaults.plist的内容,并再次构建/运行应用程序(当然,没有从设备中删除它)

    结果是:

    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
    
    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
    不正确,要么您在其他地方做了导致崩溃的事情


    在更一般的层面上,除了测试应用程序更新场景(事后建议并不重要,而且我们都以各种方式艰难地学习了这一点),我的良好实践是确保你的应用程序即使在出现错误或不可预见的输入时也不会崩溃

    “新功能尝试写入升级后不存在的密钥“:这应该不是什么问题。如果密钥不存在,则会创建该密钥。你能发布你的代码或详细说明你实际上在做什么吗?@sergio请看我编辑的问题。旁注。在提交给苹果之前,你没有测试过你的应用程序的用户升级吗?在向Apple提交更新之前,您必须始终测试干净的安装,并且必须测试应用程序先前版本的升级。这些都必须使用干净的版本来完成,以确保旧文件被错误地遗留下来。显示注册默认值和处理
    Delta
    键的代码。@rmaddy我必须承认我没有测试用户更新场景,只有干净的安装。那太愚蠢了,所以我在那里学到了宝贵的一课。