删除多个核心数据对象:NSFetchedResultsController 问题

Delete multiple Core Data objects: Issue with NSFetchedResultsController

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

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

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

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

(其中一些是我的库代码)。 删除两个对象后,出现崩溃并显示以下日志消息:

CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (99) must be equal to the number of items contained in that section before the update (101), plus or minus the number of items inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out). 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。

如果您删除多个图像,然后保存上下文,则 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.

中包含一些很好的解释和可能的实施