Ios7 为什么在电池电量耗尽后不读取NSUserDefaults 7

Ios7 为什么在电池电量耗尽后不读取NSUserDefaults 7,ios7,nsuserdefaults,Ios7,Nsuserdefaults,我正在使用NSUserDefaults来存储应用程序EULA和PP是否已被接受(除其他事项外),这通常可以正常工作。我可以启动,退出,然后返回到应用程序,它读取的值很好。我可以关闭应用程序并重新启动-读取默认值。我可以重新启动手机,然后重新启动应用程序,它可以读取默认值 但当手机在电池电量不足的情况下重新启动时,我打开应用程序,并被提示再次接受我的EULA和PP。这只发生在IOS7上的iPhone5上。我在IOS6上有一个3GS,它没有表现出相同的行为 我怀疑这可能与解决的问题类似,但这涉及到密

我正在使用NSUserDefaults来存储应用程序EULA和PP是否已被接受(除其他事项外),这通常可以正常工作。我可以启动,退出,然后返回到应用程序,它读取的值很好。我可以关闭应用程序并重新启动-读取默认值。我可以重新启动手机,然后重新启动应用程序,它可以读取默认值

但当手机在电池电量不足的情况下重新启动时,我打开应用程序,并被提示再次接受我的EULA和PP。这只发生在IOS7上的iPhone5上。我在IOS6上有一个3GS,它没有表现出相同的行为

我怀疑这可能与解决的问题类似,但这涉及到密钥链中的权限问题。同样的权限问题是否也适用于NSUserDefaults


有没有人在IOS7上遇到过类似的NSUserDefaults问题?

因此,通过实验和谷歌搜索,我得出如下结论:

这里的问题在于重大的更新更改以及在锁定屏幕后启动应用程序的任何过程。[NSUserDefaults defaultUser]加载的文件.plist受到保护(NSFileProtectionCompleteAntilFirstUserAuthentication),因此只有在应用程序启动后第一次解锁后才能访问该文件。因此,如果某个进程在后台启动你的应用程序,而你的应用程序试图访问defaultUser用户默认值,它将无法加载该文件,因此会给你一组新的用户默认值

在我的例子中,这里发生的事情是,应用程序随后进入等待EULA和PP被接受的状态,因为它从默认值(无法读取)读取到它们尚未被接受。解锁手机并重新打开应用程序(请注意,该应用程序已“启动”)后,有些进程会写入NSUserDefaults,有些进程在我的应用程序中,有些进程在我的应用程序使用的库中。在大多数情况下,我都是在默认值上调用synchronise,因此会清除无法读取的旧默认值。我想很多人都是这样

有几种不同的解决方案

首先,我编写了一个相当于NSUserDefaults的类,它包装了一个NSMutableDictionary,并将字典保存到库/应用程序支持中的.plist中。我将文件的保护更改为NSFileProtectionNone。注意:如果在此文件中存储敏感信息,则不建议这样做。还请注意,每次写入文件时都必须设置文件的权限。比如:

NSError *error;   
BOOL saved = [defaultsDic writeToURL:defaultsFileUrl atomically:YES];
[[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey]ofItemAtPath:[defaultsFileUrl path] error:&error];
这个方法工作得很好,但事实证明,我在从钥匙链读写数据时遇到了另一个问题。请看我上面问题中的链接,这是相同的问题。在应用程序启动后第一次解锁之前,钥匙链值具有相同的保护。我不想从钥匙链中删除保护,事实上,我也不想从自定义用户默认设置中删除保护

所以下一个解决方案就是实际解决这个问题。如果应用程序在锁定屏幕后启动,请不要尝试访问受保护的数据!这意味着我必须检测到应用程序在锁定屏幕后启动,然后等待应用程序解锁,然后才能继续读取我的用户默认值和钥匙链值

第一个要求是在应用程序启动时检查受保护的数据是否可用,因此可能在ApplicationIDLaunch或其他合适的地方

[[UIApplication sharedApplication]isProtectedDataAvailable]
如果在应用程序启动时这不是真的,那么你就在锁屏后面。此时您应该暂停,不要进行任何访问NSUserDefaults或Keychain(或任何受保护的文件!)的操作。然后需要等待受保护数据可用的信号。当用户解锁锁定屏幕时,appDelegate接收以下信息:

-(void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application
一旦收到,您就可以继续执行您的应用程序

在我的例子中,我在一个单例类中控制一切。创建该类时(仅在应用程序启动时发生),我会检查受保护的数据是否可用,并向NSNotificationCenter订阅相同的通知:

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationProtectedDataDidBecomeAvailable) name:UIApplicationProtectedDataDidBecomeAvailable object:nil];

因此,使用第二种方法,问题得到了解决,数据得到了保护,每个人都很高兴。

还有其他类似的未回答问题。看起来可能是一个未解决的错误。您可以使用NSCoder或核心数据解决此问题。感谢您的回答,您知道当屏幕锁定时,NSDocumentDirectory中的文件表现为NSUserDefaults是否正常吗?@muldercnc非常感谢您分享此信息。如果用户在后台应用程序刷新启动30分钟后解锁屏幕,那么我们仍然可以在后台获取数据吗?(因为离操作系统中断还有不到一分钟)还有,UIBackgroundFetchResult的完成处理程序怎么办?@Serluca我相信是这样的。因为它会生成一个事件,所以你的应用程序将响应该事件,并且即使在30分钟后也应该在后台愉快地响应该事件。当然,你得试试才能确认。我使用Xcode中的设备控制台来监视输出Xcode=>Window=>Devices,使用您正在测试的设备的控制台选项(因为您可以在调试时重新启动设备)。当处于锁定状态时,如何启动应用程序?在没有用户交互的情况下,如何由另一个应用程序启动应用程序?@user392412 Location services会因重大更新更改而启动您的应用程序,如果您已订阅这些应用程序。事实上,在一些不同的情况下,操作系统会启动你的应用程序。例如,区域监测。在用户重新启动手机或关闭应用程序后,这些功能有助于保持应用程序的活动状态。