NSUserDefaults在电话重新启动但不解锁时丢失其键和值
我们目前正在使用我们的iPhone应用程序遇到以下奇怪的问题。 正如标题所说, NSUserDefaults
正在丢失我们的自定义键和值,当电话重新启动,但没有解锁,这是发生在一个非常具体的情况。
语境:
-
我们在应用程序中使用
NSUserDefaults
来存储用户数据(例如用户名)。 -
我们的应用在背景模式下启用了位置。
-
我们遇到这个问题,只有通过空中分发或通过Testflight。 如果我使用Xcode将.ipa(相同的分布在空中)拖放到手机中,则不会遇到此问题。
情况:用户安装应用程序,login并成功将用户名存储在NSUserDefaults
。 然后,用户将他们的设备closures并将其重新打开,并让手机静置一段时间,然后解锁屏幕。
问题:如果在这段时间内触发了一个重要的位置变化,应用程序就会在后台运行,但NSUserDefaults
是空的(只有一些来自苹果的密钥,但没有我们的自定义密钥)。 然后,无论你做什么, NSUserDefaults
不会得到这个键的恢复(例如,如果你解锁你的手机并打开应用程序,你将看到键仍然丢失)。
任何帮助或想法将真正赞赏:)
过了一段时间,苹果认识到这是一个官方的错误。 所以我们只剩下不同的解决方法,直到解决:
-
如果您在解锁手机之前需要数据,请使用以下选项之一并设置
NSPersistentStoreFileProtectionKey = NSFileProtectionNone
选项:- 使用核心数据保存数据。 (如果您需要访问数据库
当电话还没有解锁的背景,你没有
在它明智的信息,你可以添加到选项arrays的
以下选项:NSPersistentStoreFileProtectionKey =
NSFileProtectionNoneNSPersistentStoreFileProtectionKey =
NSFileProtectionNoneNSPersistentStoreFileProtectionKey =
)
NSFileProtectionNone - 使用钥匙串。
- 使用.plist文件。
- 使用自定义文件:(例如:特定格式的.txt)。
- 任何其他的方式,你可能会觉得存储数据的舒适。
select你的;)
- 使用核心数据保存数据。 (如果您需要访问数据库
-
如果您在手机解锁之前不需要或不关心数据,则可以使用此方法(谢谢@maxf):
注册到applicationProtectedDataDidBecomeAvailable:
通知并在callback中执行以下代码行[NSUserDefaults resetStandardUserDefaults]
这将使你NSUserDefault
在你的手机被授权访问受保护的数据后NSUserDefault
重新加载,帮助你完全避免这个问题。
感谢所有的帮助!
我有一个非常类似的问题。 背景的应用程序。 使用其他内存大量的应用程序,直到我的应用程序从内存抛弃。 (你可以观察到这个事件,如果你的设备被插入,xcode运行的时候,Xcode会告诉你“由于内存压力,应用程序被终止了”),如果你的应用程序注册了后台获取事件,点,并重新启动,但在后台。此时,如果您的设备被locking,您的NSUserDefaults将为空。
在debugging这个案子好几天之后,我意识到并不是NSUserDefaults被破坏或者被淹没了,而是应用程序由于设备locking而无法访问它。 如果您手动尝试通过xcode组织器手动尝试下载应用程序内容,则可以实际观察到此行为,但是如果设备保持locking状态,则会注意到存储NSUserDefaults设置的plist不存在。
好的,如果应用程序在设备被locking时启动到后台,则NSUserDefaults不可访问。 没有什么大不了的,但最糟糕的部分是,一旦应用程序启动到后台,它就停留在内存中。 此时,如果用户解锁设备并将应用程序启动到前台,那么您仍然没有任何NSUserDefaults内的任何东西。 这是因为一旦应用程序已经加载NSUserDefaults到内存(这是空的),它不知道一旦设备解锁后,重新加载它。 同步在这种情况下什么都不做。 我发现解决了我的问题是呼唤
applicationProtectedDataDidBecomeAvailable
方法内的[NSUserDefaults resetStandardUserDefaults]
。
希望这有助于某人。 这些信息本可以为我节省很多小时的悲痛。
在启用密码的设备上使用重大的位置更改时,我们也遇到了此问题。 用户甚至解锁密码之前,应用程序启动BG,UserDefaults什么也没有。
我认为最好是在发生同步之前终止应用程序,因为下面的原因:
- 在UserDefaults被这个bug清除之后,UserDefaults的同步不应该被执行一次。
- 我们不能严格控制同步的调用,因为我们使用了许多第三方库。
- 如果UserDefaults无法加载(甚至在用户通过密码locking之前),该应用程序将无法正常工作。
所以这是我们的(有点奇怪)解决方法。 应用程序在检测到状况(应用程序状态= BG,UserDefaults被清除,iOS> = 7)时立即自杀。
它不应该违反UX标准,因为在后台终止应用程序将不会被用户注意到。 (也发生在用户甚至通过密码validation之前)
#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 .... }
这仍然是IOS 9.0的行为,自IOS 7.0以来。
我怀疑苹果公司不会改变这一点,因为这是由NSFileProtectionCompleteUntilFirstUserAuthentication保护.plist [NSUserDefaults standardUserDefaults]加载的结果。
另请参见为什么在平板电池IOS7之后NSUserDefaults不能读取