我们目前正在使用我们的iPhone应用程序遇到以下奇怪问题。正如标题所说,NSUserDefaults
正在丢失我们的自定义键和值,当手机重新启动但未解锁时,这种情况发生在非常具体的情况下。
背景:
我们正在使用应用中的NSUserDefaults
来存储用户数据(例如用户名)。
我们的应用已在后台模式启用了位置。
我们只在通过无线方式或通过Testflight进行分发时遇到此问题。如果我使用Xcode将.ipa(同样是通过空中传播的)拖放到我的手机中,我就不会遇到这个问题。
情况:用户安装应用程序,登录并且用户名成功存储在NSUserDefaults
上。然后,用户关闭他们的设备并将其重新打开,让手机在解锁屏幕前坐一会儿。
问题:如果在那段时间内触发了重要的位置更改,应用程序将在后台运行,但NSUserDefaults
为空(仅有来自apple的一些键,但没有我们的自定义键)。然后,无论您做什么,NSUserDefaults
都不会恢复此密钥(例如,如果您解锁手机并打开应用程序,您将看到密钥仍然丢失)。
任何帮助或想法都将得到真正的赞赏:)
答案 0 :(得分:26)
我遇到了一个非常类似的问题。背景应用程序。使用其他内存繁重的应用程序,直到我的应用程序从内存中被抛弃。 (如果您的设备已插入且xcode正在运行构建,您可以观察此事件.Xcode将告诉您"应用程序因内存压力而终止)。如果您的应用程序已注册为后台获取事件,则从此处开始,它将在某个时刻唤醒并重新启动,但会进入后台。此时,如果您的设备已锁定,则NSUserDefaults将为null。
经过几天调试这个案例后,我意识到NSUserDefaults没有被破坏或占用,因为设备锁定导致应用程序无法访问它。如果您手动尝试通过xcode管理器下载应用程序内容,您实际上可以观察到此行为,如果您的设备保持锁定,您会注意到存储NSUserDefaults设置的plist不存在。
确定如果在设备锁定时将应用程序启动到后台,则无法访问NSUserDefaults。这不是什么大问题,但最糟糕的是,一旦应用程序启动到后台,它就会保留在内存中。此时,如果用户然后解锁设备并将应用程序启动到前台,则您仍然没有NSUserDefaults内的任何内容。这是因为一旦应用程序将NSUserDefaults加载到内存中(为空),一旦设备解锁,它就不知道重新加载它。在这种情况下,synchronize什么都不做。我发现解决了我的问题是调用
[NSUserDefaults resetStandardUserDefaults]
方法中的 applicationProtectedDataDidBecomeAvailable
。
希望这有助于某人。这些信息可以为我节省许多小时的悲伤。
答案 1 :(得分:24)
过了一段时间,Apple认为这是一个官方错误。所以我们只有不同的解决方法才能解决:
如果在手机执行之前执行时需要数据
已解锁使用以下选项之一并设置NSPersistentStoreFileProtectionKey = NSFileProtectionNone
选项:
NSPersistentStoreFileProtectionKey =
NSFileProtectionNone
)选择你的;)
如果您不需要或不关心手机之前的数据 已解锁您可以使用此方法(谢谢@maxf):
注册applicationProtectedDataDidBecomeAvailable:
通知并在回调[NSUserDefaults resetStandardUserDefaults]
这将使您在手机获得访问受保护数据的权限后立即重新加载NSUserDefault
,帮助您完全避免此问题。
谢谢大家的帮助!
答案 2 :(得分:2)
在启用密码的设备上使用重要位置更改时,我们也遇到了此问题。在用户解锁密码之前,应用程序在BG上启动,而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保护。