gpt4 book ai didi

core-data - 为什么将删除的行作为 NSFetchedResultsControllerDelegate 处理时 NSTableView 会崩溃?

转载 作者:行者123 更新时间:2023-12-04 04:20:21 24 4
gpt4 key购买 nike

我使用的是相当标准的 NSTableView + CoreData + NSFetchedResultsController 设置,相关的 View Controller 是 NSFetchedResultsControllerDelegate 来接收更改。以下是来自 View Controller 的相关代码:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?){

print("Change type \(type) for indexPath \(String(describing: indexPath)), newIndexPath \(String(describing: newIndexPath)). Changed object: \(anObject). FRC by this moment has \(String(describing: self.frc?.fetchedObjects?.count)) objects, tableView has \(self.tableView.numberOfRows) rows")

switch type {
case .insert:
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath.item], withAnimation: .effectFade)
}
case .delete:
if let indexPath = indexPath {
tableView.removeRows(at: [indexPath.item], withAnimation: .effectFade)
}
case .update:
if let indexPath = indexPath {
let row = indexPath.item
for column in 0..<tableView.numberOfColumns {
tableView.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: column))
}
}
case .move:
if let indexPath = indexPath, let newIndexPath = newIndexPath {
tableView.removeRows(at: [indexPath.item], withAnimation: .effectFade)
tableView.insertRows(at: [newIndexPath.item], withAnimation: .effectFade)
}
@unknown default:
fatalError("Unknown fetched results controller change result type")
}
}

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("tableViewBeginUpdates")
tableView.beginUpdates()
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
print("tableViewEndUpdates")
}

我知道即使删除了多行,我也应该能够以这种方式批量处理所有更新。但是,这会导致连续多次删除的崩溃。

这是 session 的日志输出,表最初有四行,所有这些都被删除:
tableViewBeginUpdates
Change type NSFetchedResultsChangeType for indexPath Optional([0, 2]), newIndexPath nil. Changed object: /… correct object info …/. FRC by this moment has Optional(0) objects, tableView has 4 rows
Change type NSFetchedResultsChangeType for indexPath Optional([0, 1]), newIndexPath nil. Changed object: /… correct object info …/. FRC by this moment has Optional(0) objects, tableView has 3 rows
Change type NSFetchedResultsChangeType for indexPath Optional([0, 0]), newIndexPath nil. Changed object: /… correct object info …/. FRC by this moment has Optional(0) objects, tableView has 2 rows
Change type NSFetchedResultsChangeType for indexPath Optional([0, 3]), newIndexPath nil. Changed object: /… correct object info …/. FRC by this moment has Optional(0) objects, tableView has 1 rows

最后一行导致崩溃:
2019-05-06 22:01:30.968849+0300 MyApp[3517:598234] *** Terminating app due to uncaught exception 'NSTableViewException', reason: 'NSTableView error inserting/removing/moving row 3 (numberOfRows: 1).'
前三个删除碰巧以“正确”顺序报告(首先删除具有较大索引 [行号] 的行)。最后一行“乱序”到达,此时其他行似乎已经从 NSTableView 中消失了。

首先如何从上下文中删除对象:我正在使用推荐的最佳实践,即让两个托管对象上下文针对同一个 NSPersistentContainer 工作,一个用于主线程中的 UI 工作,另一个用于后台/网络工作。他们观察彼此的变化。当同步上下文从网络接收到一些更改、保存它们并将它们传播到 View 上下文时,会触发此崩溃,在应用程序的其他地方使用此方法:
@objc func syncContextDidSave(note: NSNotification) {
viewContext.perform {
self.viewContext.mergeChanges(fromContextDidSave: note as Notification)
}
}

我是否误解了如何使用获取的结果 Controller 委托(delegate)?我认为 beginupdates/endupdates 调用确保“ TableView 模型”在它们之间不会改变?我应该怎么做才能消除崩溃?

最佳答案

从 fetchedResultsController 进行更新比 Apple 文档中说明的要困难。当移动和插入或移动和删除同时存在时,您共享的代码将导致此类错误。这似乎不是您的情况,但此设置也将解决它。indexPath是应用删除和插入之前的索引; newIndexPath是应用删除和插入后的索引。
对于更新,您不在乎插入和删除之前的位置 - 仅在之后 - 所以使用 newIndexPath不是 indexPath .这将修复当您同时更新和插入(或更新和删除)并且单元格未按预期更新时可能发生的错误。
对于 move代表说它在插入之前从哪里移动以及在插入和删除之后应该插入到哪里。当您进行移动和插入(或移动和删除)时,这可能具有挑战性。您可以通过保存 controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 中的所有更改来解决此问题。分成三个不同的数组,插入、删除和更新。当您收到 move在插入数组和删除数组中为其添加一个条目。在 controllerDidChangeContent:对删除数组进行降序排序,对插入数组进行升序排序。然后应用更改 - 首先删除,然后插入,然后更新。这将修复当您同时进行移动和插入(或移动和删除)时可能发生的崩溃。
我无法解释为什么你有一个乱序删除。在我的测试中,我总是看到删除是降序提供的,而插入是升序提供的。尽管如此,此设置也将解决您的问题,因为有一个步骤可以对删除进行排序。
如果您有节,则还将节更改保存在数组中,然后按顺序应用更改:删除(降序),sectionDelete(降序),sectionInserts(升序),插入(升序),更新(任何顺序)。部分不能移动或更新。
概括:

  • 有 5 个数组:sectionInserts、sectionDeletes、rowDeletes、rowInserts 和 rowUpdates
  • 在 controllerWillChangeContent 中清除所有数组
  • 在 Controller 中:didChangeObject:将 indexPaths 添加到数组中(移动是删除和插入。更新使用 newIndexPath)
  • 在 Controller 中:didChangeSection 将部分添加到 sectionInserts 或 rowDeletes 数组中
  • 在 controllerDidChangeContent: 中处理它们如下:
  • 排序行删除降序
  • 排序部分删除降序
  • 排序部分插入升序
  • 对行插入升序排序

  • 然后在一个 performBatchUpdates block 中将更改应用到 collectionView:rowDelete、sectionDelete、sectionInserts、rowInserts 和 rowUpdates 的顺序。
  • 关于core-data - 为什么将删除的行作为 NSFetchedResultsControllerDelegate 处理时 NSTableView 会崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55976212/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com