iOS 12特定问题:核心数据外部存储二进制数据损坏

iOS 12特定问题:核心数据外部存储二进制数据损坏,ios,swift,core-data,ios12,Ios,Swift,Core Data,Ios12,我花了一个工作日的大部分时间试图解决这个问题 背景 我有一个简单的核心数据模型,包括书籍和阅读会话。这些书的封面(图像)以二进制数据的形式存储,并带有“允许外部存储” 在iOS 11.4及以下版本上,一切都可以正常运行。当我保存一个新会话时,所有内容都会正确更新 问题 从iOS 12开始,当我创建一个新的阅读会话并将其链接到书籍时,大约每秒一次,core data都会生成一条SQL语句,该语句也会更新书籍封面字段,有时会导致错误的引用(指向磁盘上的文件),这通常会导致重新启动应用程序时封面为零,

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

背景

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

在iOS 11.4及以下版本上,一切都可以正常运行。当我保存一个新会话时,所有内容都会正确更新

问题

从iOS 12开始,当我创建一个新的阅读会话并将其链接到书籍时,大约每秒一次,core data都会生成一条SQL语句,该语句也会更新书籍封面字段,有时会导致错误的引用(指向磁盘上的文件),这通常会导致重新启动应用程序时封面为零,而且几乎总是在磁盘上创建封面的副本(可以在模拟器的
\u EXTERNAL\u DATA
文件夹中看到)

内存中的上下文和对象保持正确(因此UI中的所有内容都正常),直到应用程序重新启动,封面通常为零

iOS 12具体版

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

采取的步骤

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

  • 在我执行
    newSession.Book=Book
    context.save()
    之前,我通过检查
    hasChanges
    ,确保在与新会话关联之前,我的代码不会修改图书实例(以及封面)

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

  • 在关联和保存之前,我尝试使用
    objectID
    请求该书的一个实例。没有改善

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

问题

对下一步有什么想法吗

状态更新


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

更新:基本核心数据问题似乎已在iOS 12.1中得到解决(在beta 4中得到验证)。我们将在我们的应用程序中保留下面描述的解决方法,并且不会建议在短期内使用外部存储选项


在与苹果的工程师交谈并提交了文件后,我们迫不及待地等待修复,因此我们接受了挑战,转而将文件存储在文件系统上,并直接自己管理

我们考虑的另一个替代方案是迁移模型,使其不允许为blob提供外部存储,但我不知道这会对性能产生什么影响,我还担心在iOS的这一部分似乎不稳定的时候进行模型迁移,特别是在读过类似于过去的故事之后:

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

删除有点棘手,因为我们的对象是从代码中的多个位置删除的,包括级联删除。最后,我选择在托管对象的
prepareForDeletion
方法中执行此操作,但这并不理想。这里有很多关于如何最好地实施这一点的讨论:


最后,为了防止我们的应用程序在一个非可选的二进制属性因为这个错误而消失时崩溃,我在我的托管对象子类中重写了
awakeFromFetch
,以确保所有必需的属性都不是零,如果它们是零,我将它们设置为占位符图像,这样就可以保存它们,而不会导致验证失败。

我认为我遇到了完全相同的问题。对此,任何变通方法或解决方案都将不胜感激!我认为我们遇到了相同的错误,尽管有一些其他的用例。我们的经验是,将图像保存到coredata外部存储成功,但在重新启动应用程序后,图像不再可访问,并返回nil。主要iOS 12错误:(也可能与此相关:在Bugtracker上报告,越多越好。很好!我最终以几乎完全相同的方式解决了这个问题。我也迫不及待地等待苹果解决这个问题。而且我觉得从长远来看,我知道并可以直接访问其URL的文件对我来说是一个更好的解决方案。有趣的是,我们做了同样的事情。我们现在将图像存储在文件上coredata中存储有唯一UUID的系统。这个核心数据问题现在似乎在iOS 12.1 beta 4中得到了修复。@rodhan我们也遇到了这个问题,并使用了一种解决方法,但我们也遇到了两个看似相关但略有不同的崩溃。这熟悉吗?@Zsolt看起来肯定是同一个错误导致了ca使用崩溃。我回答了您的问题,并提供了一些详细信息,说明如何直接检查文件系统以查看文件是否确实存在。这可能会有帮助