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 无法识别删除的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?