2

我刚刚被烧毁了,因为我的 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 的字典是否存在,或者是否有更少的皮带和大括号方法nil

4

2 回答 2

1

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

在我的 ApplicationDelegate+(void)initialize方法中,我像这样设置键及其默认值,以确保在设置之前没有尝试触及默认值:

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

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

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

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

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

于 2013-02-23T14:22:13.170 回答
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. 运行应用程序;

结果是:

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

在更一般的层面上,除了测试应用程序更新场景(事后建议这是微不足道的,我们都以一种或另一种方式艰难地学会了它),对我来说,最好的做法是确保你的应用程序确实即使存在错误或不可预见的输入,也不会崩溃。

于 2013-02-23T17:03:15.430 回答