iOS 12特定问题:核心数据外部存储二进制数据损坏 [英] iOS 12 specific problem: Core Data External Storage Binary Data corruption

查看:91
本文介绍了iOS 12特定问题:核心数据外部存储二进制数据损坏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我花了大部分时间来解决这个问题。

I've spent the better part of a workday trying to solve this.

背景

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

I have a simple core data model, with books and reading sessions. The books have covers (images) that are stored as binary data with "Allows External Storage".

在iOS 11.4及更低版本上,所有时间都可以正常工作。当我保存一个新会话时,所有内容都会正确更新。

On iOS 11.4 and below, everything works fine all the time. When I save a new session everything gets updated properly.

问题

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

Since iOS 12, when I create a new reading session and link it to the book, about every second time, core data generates a SQL statement that also updates the book cover field, sometimes resulting in a bad reference (to file on disk) which often results in the cover being nil when restarting the app, and almost always creates duplicate copy of the cover on disk (as can be seen in Simulator's _EXTERNAL_DATA folder).

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

In-memory context and objects remain correct though (and everything in the UI is therefore OK), until the app is restarted, then the cover is often nil.

iOS 12特定

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

On iOS 12, I can deterministically reproduce the error in the simulator, on physical devices, and users have reported the error as well. I cannot reproduce the error on iOS 11.4, and no users reported the error previous to iOS 12.

已采取的步骤


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

  • I've enabled "-com.apple.CoreData.ConcurrencyDebug 1", so it shouldn't be that I'm accessing anything from the wrong queue. I've also enabled "-com.apple.CoreData.SQLDebug 3" so that I can see exactly what gets written.

通过与 hasChanges 关联,确保在与新Session关联之前,我的代码未修改Book实例(及其封面)。 ,就在我执行 newSession.book = book context.save()之前。

I've made sure the Book instance (and therefore the cover) is not modified by my code before the association with the new Session by checking hasChanges, just before I do newSession.book = book and context.save().

为确保100%确保我没有触摸任何线程的cover属性,我已将该属性的吸气剂和吸气剂短路了。没有改善。

To be 100% sure I'm not touching the cover property on any thread I've short-circuited my getters and setters for that property. No improvement.

我尝试使用 objectID 来请求书的实例,关联并保存。没有改善。

I've tried using objectID to request an instance of the book just before the association and save. No improvement.

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

I've even tried the option where the context keeps strong references to all objects, just to make sure it was not some kind of memory management issue. No improvement.

问题

下一步有什么建议吗?

状态更新

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

This is a defect in iOS 12. See accepted answer below for a detailed description of a resonable workaround.

推荐答案

更新:潜在的核心数据问题似乎已在iOS 12.1中得到解决(已在 beta 4 中进行了验证)。我们将在我们的应用程序中保留以下所述的解决方法,并且不建议您尽快使用外部存储选项。

Update: The underlying Core Data issue appears to be resolved in iOS 12.1 (verified in beta 4). We will keep the workaround described below in our app, and won't be recommending using the External Storage option any time soon.

与苹果工程师交谈并提交了上面提到的雷达 a>,我们迫不及待地想修复它,于是我们选择了解决方案,转而将文件存储在文件系统上,然后直接由我们自己进行管理。

After talking to Apple engineers and filing the Radar mentioned above, we couldn’t wait around for a fix, so we took the hit and switched to storing files on the filesystem and managing it directly ourselves.

我们曾考虑过迁移模型,以不允许BLOB使用外部存储,但是我不知道会对性能产生什么影响,并且我还担心在iOS的这一部分似乎不稳定的情况下进行模型迁移,特别是在阅读了过去的类似故事之后:核心数据:不要将大文件存储为二进制数据– Ale xander Edge –中型

Another alternative that we considered was migrating our model not to allow External Storage for BLOBs, but I don't know what impact that would have had on performance and I was also worried about a model migration at a time when this part of iOS seems to be unstable, especially after reading stories like this in the past: Core Data: don’t store large files as binary data – Alexander Edge – Medium

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

It wasn't too much of a pain to implement local storage ourselves. You just need to have a unique identifier for each record that you can use to create a filename so you can map files to records. We added an extension to our Managed Object subclass with methods for reading, writing and deleting the files. Now, instead of calling e.g. article.photo = image.pngData(), we now need to call something like article.savePhoto(image.pngData()) and then we do similar when we want to retrieve the image. You can also add some code to these methods to support backwards compatibility with any images that are currently stored in Core Data.

删除操作有点棘手,因为删除了我们的对象从代码中的多个位置开始,包括级联删除。最后,我选择在托管对象的 prepareForDeletion 方法中执行此操作,但这并不理想。这里有很多有关如何最佳实现的讨论:可可-删除未保存核心数据对象时如何处理外部数据的清理? -堆栈溢出

Deletion was a little more tricky because our objects are deleted from multiple places in the code, including cascading deletes. In the end I opted to do it in the managed object's prepareForDeletion method but it is not ideal. There is plenty of discussion of how best to implement this here: cocoa - How to handle cleanup of external data when deleting unsaved Core Data objects? - Stack Overflow

最后,为避免当非可选二进制属性由于此错误而消失时,我们的应用崩溃,我覆盖了 awakeFromFetch ,以确保所有必需属性都不为零;如果是,则将它们设置为占位符图像,以便可以保存它们而不会导致验证失败。

Finally, to prevent our app crashing when a non-Optional binary attribute has disappeared because of this bug, I override awakeFromFetch in my Managed Object subclass to ensure that any required attributes are not nil, and if they are, I set them to a placeholder image so that they can be saved without the validation failing.

这篇关于iOS 12特定问题:核心数据外部存储二进制数据损坏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆