NSUserDefaults丢失钥匙和钥匙电话重启但未解锁时的值

时间:2013-11-28 14:53:04

标签: ios background nsuserdefaults core-location reboot

我们目前正在使用我们的iPhone应用程序遇到以下奇怪问题。正如标题所说,NSUserDefaults正在丢失我们的自定义键和值,当手机重新启动但未解锁时,这种情况发生在非常具体的情况下。

背景:

  • 我们正在使用应用中的NSUserDefaults来存储用户数据(例如用户名)。

  • 我们的应用已在后台模式启用了位置。

  • 我们只在通过无线方式或通过Testflight进行分发时遇到此问题。如果我使用Xcode将.ipa(同样是通过空中传播的)拖放到我的手机中,我就不会遇到这个问题。

情况:用户安装应用程序,登录并且用户名成功存储在NSUserDefaults上。然后,用户关闭他们的设备并将其重新打开,让手机在解锁屏幕前坐一会儿。

问题:如果在那段时间内触发了重要的位置更改,应用程序将在后台运行,但NSUserDefaults为空(仅有来自apple的一些键,但没有我们的自定义键)。然后,无论您做什么,NSUserDefaults都不会恢复此密钥(例如,如果您解锁手机并打开应用程序,您将看到密钥仍然丢失)。

任何帮助或想法都将得到真正的赞赏:)

4 个答案:

答案 0 :(得分:26)

我遇到了一个非常类似的问题。背景应用程序。使用其他内存繁重的应用程序,直到我的应用程序从内存中被抛弃。 (如果您的设备已插入且xcode正在运行构建,您可以观察此事件.Xcode将告诉您"应用程序因内存压力而终止)。如果您的应用程序已注册为后台获取事件,则从此处开始,它将在某个时刻唤醒并重新启动,但会进入后台。此时,如果您的设备已锁定,则NSUserDefaults将为null。

经过几天调试这个案例后,我意识到NSUserDefaults没有被破坏或占用,因为设备锁定导致应用程序无法访问它。如果您手动尝试通过xcode管理器下载应用程序内容,您实际上可以观察到此行为,如果您的设备保持锁定,您会注意到存储NSUserDefaults设置的plist不存在。

确定如果在设备锁定时将应用程序启动到后台,则无法访问NSUserDefaults。这不是什么大问题,但最糟糕的是,一旦应用程序启动到后台,它就会保留在内存中。此时,如果用户然后解锁设备并将应用程序启动到前台,则您仍然没有NSUserDefaults内的任何内容。这是因为一旦应用程序将NSUserDefaults加载到内存中(为空),一旦设备解锁,它就不知道重新加载它。在这种情况下,synchronize什么都不做。我发现解决了我的问题是调用

[NSUserDefaults resetStandardUserDefaults]方法中的

applicationProtectedDataDidBecomeAvailable

希望这有助于某人。这些信息可以为我节省许多小时的悲伤。

答案 1 :(得分:24)

过了一段时间,Apple认为这是一个官方错误。所以我们只有不同的解决方法才能解决:

  1. 如果在手机执行之前执行时需要数据 已解锁使用以下选项之一并设置NSPersistentStoreFileProtectionKey = NSFileProtectionNone选项:

    • 使用Core Data保存数据。 (如果您需要访问中的数据库 手机未解锁时的背景而你没有 在其中有明智的信息,你可以添加选项数组 以下选项:NSPersistentStoreFileProtectionKey =
      NSFileProtectionNone
    • 使用钥匙串。
    • 使用.plist文件。
    • 使用自定义文件:(例如:.txt具有特定格式)。
    • 您可能觉得用于存储数据的任何其他方式。

    选择你的;)

  2. 如果您不需要或不关心手机之前的数据     已解锁您可以使用此方法(谢谢@maxf):

  3. 注册applicationProtectedDataDidBecomeAvailable:通知并在回调[NSUserDefaults resetStandardUserDefaults]

    中执行以下代码行

    这将使您在手机获得访问受保护数据的权限后立即重新加载NSUserDefault,帮助您完全避免此问题。

    谢谢大家的帮助!

答案 2 :(得分:2)

在启用密码的设备上使用重要位置更改时,我们也遇到了此问题。在用户解锁密码之前,应用程序在BG上启动,而UserDefaults则没有任何内容。

我认为最好在同步发生之前终止应用,因为原因如下:

    在此错误清除UserDefaults后,不应执行一次
  • UserDefaults'同步。
  • 我们无法严格控制同步调用,因为我们使用了许多第三方库。
  • 如果无法加载UserDefaults,应用程序将无法正常运行(即使在用户通过密码锁之前)。

所以这是我们(有点奇怪)的解决方法。当检测到情况(app状态= BG,UserDefaults被清除,iOS> = 7)时,应用程序会立即自行终止。

它不应违反UX标准,因为用户甚至不会注意在后台终止应用程序。 (并且它也发生在用户甚至通过密码验证之前)

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

+ (void)crashIfUserDefaultsIsInBadState
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")
        && [UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
        if ([[NSUserDefaults standardUserDefaults] objectForKey:@"firstBootDate"]) {
            NSLog(@"------- UserDefaults is healthy now.");
        } else {
            NSLog(@"----< WARNING >--- this app will terminate itself now, because UserDefaults is in bad state and not recoverable.");
            exit(0);
        }
    }
    [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"firstBootDate"];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [self.class crashIfUserDefaultsIsInBadState]; // need to put this on the FIRST LINE of didFinishLaunchingWithOptions

    ....
}

答案 3 :(得分:1)

这仍然是IOS 9.0上的行为,自IOS 7.0以来一直存在。

我怀疑Apple不会改变这一点,因为这是因为[NSUserDefaults standardUserDefaults]加载的.plist受NSFileProtectionCompleteUntilFirstUserAuthentication保护。

另见Why are NSUserDefaults not read after flat battery IOS7