NSFetchedResultsController 无法识别删除

NSFetchedResultsController not recognizing deletes

我有多个核心数据实体,全部同步到服务器。为了进行测试,我构建了一个函数,它将: 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" 委托方法触发更新

时出现错误

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 rows in section 0. The number of rows contained in an existing section after the update (14) must be equal to the number of rows contained in that section before the update (7), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)

我已将刷新功能分成两部分,这样我就可以看到删除从未被识别,因此当更新触发并尝试 tableView.insertRows(在: ...)。 我还尝试在我的主线程上直接更新 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")
    }
}

非常感谢@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 where this slide was shown: 我尝试过使用 .refreshAll() 和 .mergeChages,但对我来说最简单的似乎是放弃我的 FRC 并使用 NSManagedObjectContextDidSave 通知

这是预期的行为,因为正如@redPanda 指出的那样,批处理 deletion/updation 直接发生在 Store 上。您可以尝试使用

刷新上下文(通常 'main' 作为 FRC 应附加到的上下文)
context.refreshAllObjects()

我结束了我的 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

        }
    }

如上所述,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()
}