删除多个Core Data对象:NSFetchedResultsController问题

时间:2017-12-03 02:05:24

标签: ios core-data nsfetchedresultscontroller

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

我有UI控件,允许用户同时删除多个图像,并且当用户删除多个对象时出现问题。删除的代码是:

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

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

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

  

CoreData:错误:严重的应用程序错误。抓住了例外   在核心数据更改处理期间。这通常是一个bug   NSManagedObjectContextObjectsDidChangeNotification的观察者。   无效更新:第0部分中的项目数无效   更新(99)后现有部分中包含的项目必须是   等于该部分之前包含的项目数   更新(101),加上或减去插入或删除的项目数   从该部分(0插入,1删除)和加号或减号   物品移入或移出该部分(0移入,0移出)。   with 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。

1 个答案:

答案 0 :(得分:1)

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

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

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

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

  1. 创建数组以跟踪更改(插入,删除)。
  2. 每次调用didChangeObject:时,将相应的indexPath添加到相关数组中。
  3. 调用controllerDidChangeContent:时,遍历数组(首先删除,插入时)调用相应的collectionView更新方法。 (然后清空阵列,为下一批更新做好准备)。
  4. this question and its answers中包含了一些很好的解释和潜在的实现。