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

查看:19
本文介绍了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 开始,当我创建一个新的阅读会话并将其链接到书籍时,大约每 次,核心数据都会生成一条 SQL 语句,该语句也会更新书籍封面字段,有时会导致错误的引用(对磁盘上的文件)通常会导致在重新启动应用程序时封面 nil,并且几乎总是在磁盘上创建封面的重复副本(如模拟器的 中所示)_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 在我执行 newSession.book = bookcontext.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 属性,我已将该属性的 getter 和 setter 短路.没有改善.

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.

问题

对下一步有什么想法吗?

Any ideas for next steps?

状态更新

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

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

推荐答案

更新:底层 Core Data 问题似乎在 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.

在与 Apple 工程师交谈并提交上述雷达后,我们无法't 等待修复,所以我们接受了打击并转而将文件存储在文件系统上并直接由我们自己管理.

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 似乎不稳定,尤其是在过去阅读过这样的故事之后:核心数据:不要将大文件存储为二进制数据 – Alexander Edge – Medium

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 方法中执行此操作,但这并不理想.这里有很多关于如何最好地实现这一点的讨论:cocoa - 删除未保存 Core Data 对象时如何清理外部数据?- 堆栈溢出

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天全站免登陆