【问题标题】:Delete multiple Core Data objects: Issue with NSFetchedResultsController删除多个核心数据对象:NSFetchedResultsController 的问题
【发布时间】:2017-12-03 02:05:24
【问题描述】:

我找到了解决这个问题的方法,但我不是很喜欢这个修复方法。我的问题是这样的。我正在使用NSFetchedResultsController 来填充 UICollectionView——它显示了一组图像。每个图像都由一个 Core Data 对象描述(例如,它的文件名在 Core Data 对象中)。

我的 UI 控件允许用户同时删除多个图像,但在用户删除多个对象时遇到了问题。删除的代码是:

    for image in images {
       CoreData.sessionNamed(CoreDataExtras.sessionName).remove(image)
    }

    CoreData.sessionNamed(CoreDataExtras.sessionName).saveContext()

(其中一些是我的库代码)。 删除两个对象后,我收到崩溃和以下日志消息:

CoreData:错误:严重的应用程序错误。异常被捕获 在核心数据更改处理期间。这通常是一个错误 NSManagedObjectContextObjectsDidChangeNotification 的观察者。 无效更新:第 0 节中的项目数无效。 更新 (99) 后包含在现有部分中的项目必须是 等于该部分中包含的项目数之前 更新(101),加或减插入或删除的项目数 从该部分(0 插入,1 删除)和加号或减号 移入或移出该部分的项目(0移入,0移出)。 与 userInfo (null)

如果我将删除代码更改为:

    for image in images {
        CoreData.sessionNamed(CoreDataExtras.sessionName).remove(image)
        CoreData.sessionNamed(CoreDataExtras.sessionName).saveContext()
    }

我猜问题是在委托回调方法中:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath {

我愿意:

collectionView.deleteItems(at: [indexPath]) 

显然,您可以在 didChangeObject 方法中执行 reloadItems,也可以在每次删除对象后执行 saveContext。

【问题讨论】:

    标签: ios core-data nsfetchedresultscontroller


    【解决方案1】:

    如果您删除多个图像,然后保存上下文,FRC 会处理所有删除 - 所以它的sectionsfetchedObjects 等反映了所有这些更改。但它随后会为每次更改单独调用didChangeObject: 委托方法。在该方法中,您调用 collectionView 更新方法(例如,deleteItems);然后,collectionView 调用它的 dataSource 方法并进行快速统计:有 X 个项目,Y 个项目被删除,现在有 Z 个项目并抛出一个错误,因为 Z != X-Y。

    当 FRC 与 tableView 一起使用时,可以通过在 FRC controllerWillChangeContent:controllerDidChangeContent: 委托方法中使用 tableView beginUpdatesendUpdates 调用来解决此问题。这会导致 tableView 推迟统计,直到处理完所有单独的更改 - 此时数字确实加起来了。

    您的解决方案 - 在每次删除后调用 saveContext - 导致 FRC 依次处理每个删除:更新其 sectionsfetchedObjects 等,以一次仅反映一个删除。这使 FRC 的数据与 collectionView 保持同步。一种可能的改进是在每次删除后对上下文调用processPendingChanges,而不是保存上下文。这样可以避免在您不想保存数据时保存数据,但仍然会导致每次删除都单独处理。

    替代方法是模仿 tableView 的 beginUpdates/endUpdates 机制来保存所有 collectionView 更新,直到所有 FRC 更新都已处理。这大致如下:

    1. 创建数组以跟踪更改(插入、删除)。
    2. 每次调用didChangeObject:,将对应的indexPath添加到相关数组中。
    3. controllerDidChangeContent:被调用时,遍历数组(先删除,插入时)调用相应的collectionView更新方法。 (然后清空数组,为下一批更新做好准备)。

    this question and its answers 中包含了一些很好的解释和潜在的实现。

    【讨论】:

    • 谢谢! -- 我可能会利用你的processPendingChanges 想法。
    • 事实证明,就集合视图而言,我不得不转向您谈到的数组解决方案——累积挂起的删除并一次完成所有操作。我对问题的原始“修复”和processPendingChanges 更改都没有完全解决我的问题。
    • 好吧,生活还在继续,我的崩溃并没有消失。即使有这些变化。原来还有别的东西,相同但不同,在继续——我有第二个视图控制器,也使用NSFetchedResultsController 和相同的核心数据对象。但是,发生了内存泄漏,并且这个视图控制器被保留了。当我在主视图控制器中删除图像时,另一个保留的视图控制器导致了崩溃——原因与上面@pbasdf 讨论的原因基本相同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-17
    • 1970-01-01
    • 2011-08-14
    • 2016-10-27
    • 2016-11-07
    相关资源
    最近更新 更多