【问题标题】:How to handle cleanup of external data when deleting Core Data objects删除 Core Data 对象时如何处理外部数据的清理
【发布时间】:2011-07-01 16:21:24
【问题描述】:

我对 Core Data 还很陌生,遇到了其他人一定遇到过的问题。

我的数据模型包括图像,我将这些图像保存在数据库外部,并简单地存储图像的路径/URL。 (在 Apple 的一份核心数据演示文稿中推荐)

删除我的图像对象时,我可以手动遍历关系并删除图像文件,但我想知道是否有更优雅的方法来执行此操作。

理想的解决方案会以某种方式与图像对象相关联,并且可以与 Core Data undo/redo 一起使用。

【问题讨论】:

  • 您的意思是您有一个单独的 Image 实体(存储图像数据的位置)与您的“主”实体有关系,还是您实际上将图像的文件路径存储在“主”中" 实体(即属性)并将图像保存在文档文件夹中?
  • 是的,我有一个与图像实体有关系的主要实体。在图像实体内部,有一个指向存储在文档文件夹中的图像的路径。我这样设置是因为我读过的所有内容都建议不要在 Core Data 中存储大图像。显然,我希望在图像实体消失时删除图像文件,并且想知道在 Core Data 中是否有一种优雅的方式来处理这个问题。

标签: iphone ios core-data


【解决方案1】:

在您的“图像”实体类中,实现willSave。检查[self isDeleted],如果是,请删除该文件。这会推迟文件的实际删除,直到保存存储,这为您提供了一些撤消能力。设置适当的级联规则以在其所有者离开时删除图像实体,然后就可以了。

[eta.:Phil Calvin 在下面的评论是正确的 - 如果您使用多个上下文,didSave 可能是一个更好的地方。]

[埃塔。很久以后:] MartinW 提出了一个很好的观点——如果对象从未被保存,将不会调用/是否保存。删除未保存的对象只是将其从上下文中取消插入并将其丢弃,没有特殊的生命周期事件。更复杂的是,您可以“撤消”和“重做”删除,包括(我认为)这种删除。

想到的几种方法:

这可能是覆盖prepareForDeletion:awakeFromSnapshotEvents: 以捕获取消删除和重新删除的情况。要支持撤消/重做,您不仅需要当场删除文件,还需要使用某种“待删除”注册表(例如,在发布保存通知时清理共享的可变文件名集)。那么 will/didSave 就不存在了。

或者,如果您可以使用 BLOB 字段而不是文件,则可以选中二进制属性上的“允许外部存储”框,将 jpeg 数据放在那里,并获得文件存储的一些(不是全部)优势没有(大部分)头痛。小二进制文件将保存在数据库中;任何大于私有阈值的东西都将被分流到一个单独的隐藏核心数据管理文件中。但是,当对象出现故障时,核心数据仍然必须将整个内容加载到 NSData 中。我将这种方法用于“用户头像”类型的小图像。

最后,如果 images 目录中没有其他内容,您可以注册 didSave 通知并在任何保存后手动清理。对所有 Image.filename 属性运行获取请求,将其与相关图像目录的目录列表进行比较,并酌情删除。我在开发过程中使用这种方法作为我的“大棒”,以确保其他一切都在做它应该做的事情。

[让我知道这些方法的成功或困难,我会及时更新。]

【讨论】:

  • 删除willSave 中的图像可能会导致您删除仍在持久存储中引用的图像。你可能想避免这种情况。详情见我的回答。
  • 只有在添加和删除“图像”之间保存了上下文时,这才有效。如果不是,则 -willSave 和 -didSave 都不会被调用。请参阅我的问题:How to handle cleanup of external data when deleting unsaved Core Data objects?
  • willSave & didSaveprepareForDeletion: 之前和之后被调用,并且在撤消/重做之后不会被调用。通过处理外部文件来支持撤消/重做的最佳解决方案应该是使用awakeFromSnapshotEvents。在这里查看:stackoverflow.com/a/25614035/886215
【解决方案2】:

在您的Image 类中,实现-didSave。在这个方法中,检查是否[self isDeleted],如果是YES,则从磁盘中删除图像文件。

-didSave 而不是-willSave 中执行此操作很重要,尤其是当您有多个与持久存储关联的托管对象上下文时。 willSave 发送before Core Data discovers and reports(作为保存错误)任何合并冲突。这意味着您可能会收到willSave

  1. 如果您实施合并对象并重新尝试保存的合并策略,则多次尝试保存
  2. 由于合并冲突而从不提交到持久存储的保存之前

过早删除图像文件可能会在以后从另一个托管对象上下文访问这些图像时导致崩溃。

【讨论】:

  • 只有在添加和删除“图像”之间保存了上下文时,这才有效。如果不是,则 -willSave 和 -didSave 都不会被调用。请参阅我的问题:How to handle cleanup of external data when deleting unsaved Core Data objects?
  • @MartinW:正确,这不会保存图像数据,除非还保存了数据库。我认为这是正确的,但与许多事情一样,这取决于应用程序的需求。
【解决方案3】:

我建议覆盖图像实体prepareForDeletion 方法来删​​除磁盘上的图像文件。该方法只会在实际删除图像实体对象时调用。

【讨论】:

  • 嗯。鉴于关于 NSManagedObject 方法的文档很少,我只需要尝试一些涉及 willSave 与 prepareForDeletion 的场景,以确定它们何时被调用。我不想陷入删除图像文件但从未发生删除的情况。 (比如说因为应用程序在执行保存之前崩溃或终止)谢谢。
  • prepareForDeletion 在涉及级联删除的子对象上发生在父关系已经无效之后。所以有点局限。我有一个案例,文件名的部分路径是从父属性派生的。噗就是这样的逻辑。
【解决方案4】:

最佳实践是(我认为这与@Rog 暗示的相同)为存储的图像提供一个实体,并使您的对象与该实体具有关系,而不是在每个对象中存储路径。这种情况下,只要找到一个代表要删除的图片的对象并删除即可,反向关系会自动失效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-19
    • 1970-01-01
    • 1970-01-01
    • 2022-11-15
    • 2013-11-02
    • 1970-01-01
    • 2015-03-13
    相关资源
    最近更新 更多