【问题标题】:Occasional NSUserDefaults trouble偶尔的 NSUserDefaults 麻烦
【发布时间】:2014-09-29 13:50:30
【问题描述】:

我使用NSUserDefaults 在用户的 iOS 设备上保留大约 100 个键/值对。每对只是一个字符串键和布尔值。这几乎一直都很好。最近,一些用户提到他们的应用程序正在“重置”。具体来说,他们的应用程序没有正确读取来自NSUserDefaults 的数据。我正在尝试了解这是如何发生的。

需要注意的几点:

  • 每次更新用户默认值后,我都会调用synchronize
  • 我没有任何代码可以清除单个条目或整个默认值
  • 默认值在application:didFinishLaunchingWithOptions:中读取
  • 应用从后台移到前台时不会读取默认值

我发现了一些有趣的 cmets in this Loom.com blog post。当应用程序在后台重新启动时,听起来像 NSUserDefaults 支持 plist 可能无法访问。如果后台应用程序在后台崩溃,我不确定它们是否会重新启动。但是,我很好奇,因为根据我的崩溃报告服务,我的应用程序确实在后台崩溃了。此外,此崩溃会在收到内存警告后立即发生。

在崩溃后(在后台)在后台重新启动时,应用是否可能无法读取用户默认值?

非常感谢任何有关如何诊断此问题的建议!

编辑 - 更多信息:听起来 CoreLocation 框架可能会导致应用在后台崩溃后在后台重新启动。我的应用程序包含一些 3rd 方广告和分析 SDK。事实上,这个问题是在添加一个可以使用 CoreLocation 的特定 SDK 后开始出现的。

【问题讨论】:

  • 键/值是否永远消失了?
  • 正在重置的键是否有通用名称?也许第三方会覆盖此键?
  • 似乎很多人都有同样的问题。查看link,希望有所帮助。
  • " 此外,此崩溃在收到内存警告后立即发生。"这意味着您的应用程序没有正确响应内存警告。
  • 您的应用程序是否曾经通过位置更改(或推送通知)在后台启动?如果您启用了“静态加密”,您的应用程序可以在后台启动(大概是在手机被锁定时)并且 NSUserDefaults 无法访问。您可以通过 [[UIApplication sharedApplication] isProtectedDataAvailable] 进行查询,如果返回 NO 您将无法访问受保护的数据(或钥匙串)

标签: ios nsuserdefaults


【解决方案1】:

我认为使用NSUserDefaults 保存 100 个键/值是错误的方法。 但是,如果您使用 NSUserDefaults 保存这 100 个键/值并且您阅读了 application:didFinishLaunchingWithOptions: 中的默认值 然后你只需恢复你也读过的

- (void)applicationWillEnterForeground:(UIApplication *)application;

【讨论】:

    【解决方案2】:

    我认为从开发的角度来看,使用NSUserDefaults 保存 100 个密钥/数据可能首先是错误的。
    最正确的方法是使用序列化的 plist 将登录/会话数据保存在钥匙串中(您可以免费加密),其余的保存在文档目录中。在NSUserDefaults 中,我会保存首选项文件的路径或首选项文件的版本号。
    如果有触发它的触发器,例如CoreLocation 或通知,您的应用程序可以在后台崩溃后重新启动。
    我真的怀疑NSUserDefaults 在后台不可用(它也是线程安全的),我认为这将是一个如此大的问题,以至于 SO 会有很多类似的问题。
    您在后台收到内存警告的事实可能与崩溃的原因有关。崩溃日志说什么?你能贴出来吗?
    当您的应用程序暂停时,内存占用是多少?您是否有一些由内存警告或应用生命周期通知触发的方法?

    【讨论】:

      【解决方案3】:

      iOS 做了一些复杂的事情来(几乎)无缝地加密写入磁盘的数据,所以这种错误绝对是可能的。也许文件由于某种原因无法解密,而是被删除,恢复NSUserDefaults

      我不知道这是什么原因,但在我看来很可能。

      另外,请注意 NSUserDefaults 将数据保存到不安全的位置 <Application_Home>/Library。它适用于“您的应用程序下载或生成并可以根据需要重新创建的文件”。

      也许存储数据的更好位置是<Application_Home>/Documents,它用于“无法由您的应用重新创建”的数据。如果您的用户默认设置非常重要以至于成为问题,那么它将归类为“用户生成的内容”,因此应存储在 Documents 文件夹中。

      所以,我建议删除 NSUserDefaults,因为它不能满足您的需求,并通过使用 NSCoding 或 Binary Plist 将 NSDictionary 写入 Documents 文件夹来保存数据(确保将其设置为 NSPropertyListBinaryFormat_v1_0 ,因为这不是默认设置,应该在 iOS 设备等慢速闪存上使用)。

      Apple 有很好的 NSCoding 和 Plist 序列化的文档和示例代码:

      https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Archiving/Articles/creating.html#//apple_ref/doc/uid/20000949-BABGBHCA

      https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/PropertyLists/SerializePlist/SerializePlist.html#//apple_ref/doc/uid/10000048i-CH7-SW1

      您还可以使用核心数据(我在我的应用程序中使用的)或 SQLite。但是,如果您只存储“数百个”设置,我不会使用这些选项中的任何一个。如果数据不适合 RAM,它们通常是一个不错的选择。对于确实适合内存的数据,NSCoding 和 Plist 明显更快更容易使用。

      同时阅读“你应该把你的应用程序的文件放在哪里”:https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html

      【讨论】:

      • 我几乎同意一切,只有一件事我想指出或有更多线索:'/Library which is not a safe location' 在我看来,这是仅对 Caches 子目录有效,如果系统回收更多磁盘空间,则可以删除其内容。除了删除应用程序之外,我在文档中找不到任何提示可以删除 NSUserDefault。
      • @Andrea 在我提供的最后一个链接中,Apple 谈到了库中可以存储数据的每个位置,而应用程序支持是此用例唯一有效的位置。他们将“支持文件”归类为“您的应用程序下载或生成并可以根据需要重新创建的文件”。这意味着他们保留删除它们的权利,如果不是现在,那么在未来的 iOS 版本中。
      • IMO 加密问题没有任何意义,除了 100%。
      • @Zaph 你看到数据保护在 iOS 上的工作原理了吗?有一些保护模式,应用程序在后台运行时无法读取数据,因为用于数据加密的私钥不存在且无法生成。但是,公钥确实存在,因此您可以写入磁盘但不能从中读取 - 这似乎与应用程序的后台执行有关。这可能会导致NSUserDefaults 退回到删除它的 plist 文件,恢复到默认设置。这只是一个理论,但我认为这是一个合理的理论。
      • 实际上,它被存储到应用程序沙箱内的 Library/Preferences 中。
      猜你喜欢
      • 2011-07-12
      • 1970-01-01
      • 2013-05-09
      • 2012-11-02
      • 1970-01-01
      • 1970-01-01
      • 2011-03-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多