NSFetchedResultsController 无法识别删除
Posted
技术标签:
【中文标题】NSFetchedResultsController 无法识别删除【英文标题】:NSFetchedResultsController not recognizing deletes 【发布时间】:2017-04-25 12:30:10 【问题描述】:我有多个核心数据实体,全部同步到服务器。为了测试,我构建了一个函数,它将: 1.删除所有核心数据实体 2.调用API刷新所有数据
// Next delete data in all entities (but not user)
for entity in CoreDataStack.sharedInstance.allEntities()
if (entity != "User")
if (DataAccess.deleteExisting(entityName: entity, inMoc: self.operationMOC))
print("Deleted OK: \(entity)")
else
print("ERROR deleting \(entity)")
....
// Save updates
self.operationMOC.performAndWait
do
try self.operationMOC.save()
catch
fatalError("ERROR: Failed to save operation moc: \(error)")
self.mainThreadMOC.performAndWait
do
try self.mainThreadMOC.save()
catch
fatalError("ERROR: Failed to save main moc: \(error)")
问题是控制我的 UI 的 NSFetchedResultsController 似乎无法识别删除,当“didChange”委托方法触发更新时会出错
错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常。这通常是 NSManagedObjectContextObjectsDidChangeNotification 观察者中的一个错误。无效更新:第 0 节中的行数无效。更新后现有节中包含的行数 (14) 必须等于更新前该节中包含的行数 (7),加上或减去数字从该部分插入或删除的行数(1 插入,0 删除)加上或减去移入或移出该部分的行数(0 移入,0 移出)。与 userInfo (null)
我已将刷新功能分成两部分,因此我可以看到删除永远不会被识别,因此当更新触发并尝试 tableView.insertRows(at: ...) 时 UITableView 行太多。 我还尝试在我的主线程上直接更新 MOC - 不走运
我也加入了观察者:
NotificationCenter.default.addObserver(self, selector: #selector(MessagesTab.dataChanged), name: NSNotification.Name.NSManagedObjectContextObjectsDidChange, object: self.mainThreadMOC)
在删除和更新时完美触发,所以 Core Data 正在做它的工作。
因此,在我转储 NSFetchResults 控制器并使用通知滚动我自己之前,我的问题是,有人知道我在这里做错了什么吗? (在代码或期望中) 过去一天我一直被这件事难住了,所以任何建议都将不胜感激。
我的 NSFetchedResultsControllerDelegate didChange 方法:
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?)
print(">>>> FRC Change Fired")
switch type
case .insert:
tableView.insertRows(at: [newIndexPath!], with: .automatic)
case .delete:
tableView.deleteRows(at: [indexPath!], with: .automatic)
case .update:
let rowMessage = self.fetchedResultsController.object(at: indexPath!)
if let cell = self.tableView.cellForRow(at: indexPath!) as? InterestsListCell
cell.configureWithData(rowMessage)
default:
print("Was not a update, delete or insert")
【问题讨论】:
您需要发布您的 fetchResultsDelegate didChangeObject 方法,这就是问题所在。 @SeanLintern88,感谢您的快速响应。委托方法贴出来了,虽然我觉得没有太大问题 在tableView的numberOfItemForSection中,你引用的是FRC.fetchedObjects.count还是自存储数组? 它崩溃的原因是您的tableView的数据源没有与数据源对象对齐。 您是否使用 BatchDeleteRequest 进行删除?这些类型的更改不会触发 FRC,因此我避免使用它们。 【参考方案1】:非常感谢@JonRose 此代码不会导致 FRC 触发
// Delete all records for the entity
func deleteExisting(entityName: String, inMoc moc: NSManagedObjectContext) -> Bool
do
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
try moc.execute(deleteRequest)
return true
catch
print("ERROR: deleting existing records - \(entityName)")
return false
随后我对此进行了研究,并希望对其他人有所帮助 - 请参阅 WWDC 2015 session on Core Data 显示此幻灯片的位置: 我尝试过使用 .refreshAll() 和 .mergeChages,但对我来说最简单的方法似乎是放弃我的 FRC 并使用 NSManagedObjectContextDidSave 通知
【讨论】:
谢谢!!!我刚刚看了这个,但我仍然错过了该幻灯片的第一行 - 如果您仍然遇到此问题但想使用 FRC,请查看下面我最终做了什么【参考方案2】:这是预期的行为,因为正如@redPanda 指出的那样,批量删除/更新直接发生在 Store 上。您可以尝试使用
刷新上下文(通常是“主”作为 FRC 应附加到的上下文)context.refreshAllObjects()
【讨论】:
我试过了,但它对 FRC 不起作用【参考方案3】:我最终取消了我的 FRC,然后在批量删除完成后重建它
fetchedResultsController = nil
dataProvider?.deleteAllAlertsBatch error in
DispatchQueue.main.async
print(" done delete \(error)")
self.loadSavedData() // <- build the FRC again here - it will find 0 records
【讨论】:
【参考方案4】:如上所述,NSBatchDeleteRequest
不反映上下文的变化。取消 FRC 可能会导致在添加新 FRC 时更新 UITableView
时出现问题。
context.refreshAllObjects()
是要走的路,但要添加到@infinateLoop 的答案(我无法评论):
func deleteAndUpdate()
DeleteRecords(moc: context) // your NSBatchDeleteRequest function here
context.refreshAllObjects()
try? frc.performFetch() // catch error if required
myTableView.reloadData()
【讨论】:
以上是关于NSFetchedResultsController 无法识别删除的主要内容,如果未能解决你的问题,请参考以下文章