【问题标题】:Why are NSUserDefaults not read after flat battery IOS7为什么IOS7没电后不读取NSUserDefaults
【发布时间】:2013-12-26 05:28:44
【问题描述】:

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

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

我怀疑这可能与已解决的问题 here 类似,但这是指钥匙串中的权限问题。相同的权限问题是否适用于 NSUserDefaults?

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

【问题讨论】:

  • 关于 SO 还有其他类似的未回答问题。看起来可能是一个未解决的错误。你可以使用 NSCoder 或 Core Data 来解决它。

标签: ios7 nsuserdefaults


【解决方案1】:

所以经过实验和谷歌搜索后,我得出的结论如下:

重大更新更改以及实际上在锁定屏幕后启动应用程序的任何进程都是这里的问题。 [NSUserDefaults defaultUser] 加载的文件 .plist 受到保护(NSFileProtectionCompleteUntilFirstUserAuthentication),因此在启动应用程序后第一次解锁之前无法访问它。因此,如果一个进程在后台启动您的应用,并且您的应用尝试访问 defaultUser 用户默认值,则它无法加载该文件,因此会为您提供一组新的空白用户默认值。

在我的案例中发生的情况是,该应用程序随后进入等待 EULA 和 PP 被接受的状态,因为它从默认值(无法读取)读取它们尚未被接受.解锁手机并重新打开应用程序后——请注意它已经“启动”——有一些进程写入 NSUserDefaults,一些在我的应用程序中,一些在我的应用程序使用的库中。在大多数情况下,我在默认值上调用同步,因此炸毁了无法读取的旧默认值。我想这可能是很多人的情况。

有几种不同的解决方案。

首先,我编写了一个等效于 NSUserDefaults 的类,包装了 NSMutableDictionary 并将字典保存到 Library/Application Support 中的 .plist 中。我将文件的保护更改为 NSFileProtectionNone。请注意,如果您在此文件中存储敏感信息,则不建议这样做。另请注意,每次编写文件时都必须设置文件的权限。比如:

NSError *error;   
BOOL saved = [defaultsDic writeToURL:defaultsFileUrl atomically:YES];
[[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey]ofItemAtPath:[defaultsFileUrl path] error:&error];

这种方法效果很好,但事实证明我从钥匙串读取和写入的数据存在另一个问题。请参阅上面我的问题中的链接,这是同样的问题。钥匙串值具有相同的保护,直到应用程序启动后的第一次解锁。我不想从钥匙串中删除保护,实际上我对从我的自定义用户默认值中删除保护也不是很舒服。

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

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

[[UIApplication sharedApplication]isProtectedDataAvailable]

如果在启动应用时不是这样,那么您就处于锁定屏幕的后面。此时您应该暂停并避免任何访问 NSUserDefaults 或 Keychain(或任何受保护的文件!)的操作。然后,您需要等待受保护数据可用的信号。当用户解锁锁定屏幕时,appDelegate 会收到以下信息:

-(void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application

收到通知后,您可以继续执行您的应用程序。

在我的例子中,我控制了单例类中的所有内容。创建该类时(仅在应用启动时发生),我检查受保护的数据是否可用并订阅 NSNotificationCenter 以获得相同的通知:

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

所以用第二种方法,问题解决了,数据仍然受到保护,每个人都很开心。

【讨论】:

  • 感谢您的回答,您知道屏幕锁定时 NSDocumentDirectory 中的文件表现得像 NSUserDefaults 是否正常?
  • @muldercnc 非常感谢分享这个。如果用户在后台应用刷新启动 30 分钟后解锁屏幕,那么我们还能在后台获取数据吗? (因为我们还有不到一分钟的时间 OS 切断我们)另外,如何处理 UIBackgroundFetchResult 的完成处理程序?
  • @Serluca 我相信是的。由于它会生成一个事件,因此您的应用程序将响应该事件,并且即使在 30 分钟后也应该在后台愉快地执行此操作。当然,您必须尝试确认。我使用 Xcode 中的设备控制台来监控输出 XCODE=>Window=>Devices,使用您正在测试的设备的控制台选项(因为您可以在调试时重新启动设备)。
  • 锁定状态下如何启动应用?另一个应用如何在没有用户交互的情况下启动应用?
  • @user392412 位置服务会因重大更新更改而启动您的应用(如果您已订阅)。实际上,操作系统会在几种不同的情况下启动您的应用程序。例如。区域监控。这些对于在用户重新启动手机或终止应用后保持应用运行很有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-03
  • 1970-01-01
相关资源
最近更新 更多