【问题标题】:iOS 12 specific problem: Core Data External Storage Binary Data corruptioniOS 12 特定问题:Core Data External Storage Binary Data 损坏
【发布时间】:2019-03-06 07:04:44
【问题描述】:

我花了一个工作日的大部分时间来解决这个问题。

背景

我有一个简单的核心数据模型,包含书籍和阅读会话。这些书的封面(图片)以二进制数据的形式存储为“允许外部存储”。

在 iOS 11.4 及更低版本上,一切正常。当我保存一个新会话时,一切都会正确更新。

问题

从 iOS 12 开始,当我创建一个新的阅读会话并将其链接到图书时,大约每 次,核心数据都会生成一条 SQL 语句,该语句也会更新图书封面字段,有时会导致一个错误的引用(对磁盘上的文件),这通常会导致重新启动应用程序时封面为 nil,并且几乎总是会在磁盘上创建封面的重复副本(如 Simulator 的 _EXTERNAL_DATA 所示文件夹)。

内存中的上下文和对象仍然正确(因此 UI 中的一切都正常),直到应用程序重新启动,然后封面通常是 nil

iOS 12 特定

在 iOS 12 上,我可以确定性地在模拟器中、物理设备上重现该错误,并且用户也报告了该错误。我无法在 iOS 11.4 上重现该错误,并且在 iOS 12 之前没有用户报告该错误。

采取的步骤

  • 我已启用“-com.apple.CoreData.ConcurrencyDebug 1”,因此不应该是我从错误的队列中访问任何内容。我还启用了“-com.apple.CoreData.SQLDebug 3”,这样我就可以准确地看到写入的内容。

  • 在我做newSession.book = bookcontext.save() 之前,我通过检查hasChanges 确保我的代码在与新会话关联之前没有修改Book 实例(以及封面) .

  • 为了 100% 确定我没有触及任何线程上的封面属性,我已经短路了该属性的 getter 和 setter。没有改善。

  • 我尝试使用objectID 在关联之前请求图书的实例并保存。没有改善。

  • 我什至尝试了上下文保持对所有对象的强引用的选项,只是为了确保它不是某种内存管理问题。没有改善。

问题

对下一步有什么想法吗?

状态更新

这是 iOS 12 中的一个缺陷。有关合理解决方法的详细说明,请参阅下面接受的答案。

【问题讨论】:

  • 我想我遇到了完全相同的问题。对此的任何解决方法或解决方案将不胜感激!
  • 我认为我们遇到了同样的错误,尽管还有一些其他用例。 forums.developer.apple.com/thread/109189
  • 我们体验到将图片保存到coredata外部存储成功,但是重启应用后,图片无法访问并返回nil。 iOS 12 的主要错误。 :(
  • 在 Bugtracker 上报告,越多越好。

标签: ios swift core-data ios12


【解决方案1】:

更新:底层核心数据问题似乎在 iOS 12.1 中得到解决(在 beta 4 中得到验证)。我们将在我们的应用程序中保留下面描述的解决方法,并且不会很快推荐使用 外部存储 选项。


在与 Apple 工程师交谈并提交 Radar mentioned above 后,我们迫不及待地等待修复,因此我们接受了打击,转而将文件存储在文件系统上并直接由我们自己管理。

我们考虑的另一种选择是迁移我们的模型以不允许对 BLOB 进行外部存储,但我不知道这会对性能产生什么影响,而且我还担心模型迁移在这部分iOS似乎不稳定,尤其是在过去读过这样的故事之后:Core Data: don’t store large files as binary data – Alexander Edge – Medium

我们自己实现本地存储并没有太大的痛苦。您只需为可用于创建文件名的每条记录提供唯一标识符,以便将文件映射到记录。我们为托管对象子类添加了一个扩展,其中包含读取、写入和删除文件的方法。现在,而不是调用例如article.photo = image.pngData(),我们现在需要调用类似article.savePhoto(image.pngData()) 的方法,然后在我们想要检索图像时执行类似操作。您还可以向这些方法添加一些代码,以支持与当前存储在 Core Data 中的任何图像的向后兼容性。

删除有点棘手,因为我们的对象是从代码中的多个位置删除的,包括级联删除。最后,我选择在托管对象的prepareForDeletion 方法中执行此操作,但这并不理想。这里有很多关于如何最好地实现这一点的讨论:cocoa - How to handle cleanup of external data when deleting unsaved Core Data objects? - Stack Overflow

最后,为了防止我们的应用程序在非可选二进制属性因为这个错误而消失时崩溃,我在我的托管对象子类中覆盖 awakeFromFetch 以确保任何必需的属性都不为零,如果它们是,我将它们设置为占位符图像,以便在验证失败的情况下保存它们。

【讨论】:

  • 不错!我最终以几乎完全相同的方式解决了这个问题。我也等不及苹果解决了这个问题。另外我觉得从长远来看,我知道并且可以直接访问其 URL 的文件对我来说是一个更好的解决方案。
  • 有趣的是,我们做了同样的事情。我们现在将图像存储在具有唯一 UUID 的文件系统上,该 UUID 存储在 coredata 中。
  • 这个 Core Data 问题现在似乎在 iOS 12.1 beta 4 中得到修复。
  • @rodhan 我们也遇到了这个问题并使用了解决方法,但我们也遇到了一些看起来相关但有点不同的崩溃。这熟悉吗? stackoverflow.com/questions/53300900/…
  • @Zsolt 看起来肯定是同一个错误导致了导致崩溃的数据损坏。我回答了您的问题,详细说明了如何直接检查文件系统以查看文件是否实际存在。这可能会对你有所帮助。
猜你喜欢
  • 2013-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-30
  • 1970-01-01
相关资源
最近更新 更多