使用 NSFetchedResultsController w/NSBatchDeleteRequest

Using NSFetchedResultsController w/NSBatchDeleteRequest

我在使用 NSBatchDeleteRequest 后从 NSFetchedResultsControllerDelegate 获取准确更新时遇到问题,这些更改作为更新类型而不是 didChange 委托方法上的删除类型出现。有没有办法让更改以删除的形式出现? (我有不同的删除和更新方案)。

删除请求: (使用调用者提供的私有上下文)

fileprivate class func deletePeopleWith(ids: Set<Int>, usingContext context: NSManagedObjectContext) {

    let fr: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Person")

    var predicates: [NSPredicate] = []
    ids.forEach {

        predicates.append(NSPredicate(format: "id = %ld", [=11=]))
    }

    fr.predicate = NSCompoundPredicate(orPredicateWithSubpredicates: predicates)

    let dr = NSBatchDeleteRequest(fetchRequest: fr)
    dr.affectedStores = context.parent?.persistentStoreCoordinator?.persistentStores

    do {

        try context.parent?.execute(dr)
        context.parent?.refreshAllObjects()
        print("Deleting person")

    } catch let error as NSError {

        let desc = "Could not delete person with batch delete request, with error: \(error.localizedDescription)"
        debugPrint(desc)
    }

}

结果:

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

    DispatchQueue.main.async {

        let changeDesc = "Have change with indexPath of: \(indexPath), new index path of: \(newIndexPath) for object: \(anObject)"
        var changeType = ""
        switch type {

        case .delete:
            changeType = "delete"

        case .insert:
            changeType = "insert"

        case .move:
            changeType = "move"

        case .update:
            changeType = "update"

        }

        print(changeDesc)
        print(changeType)
    }

}

正在打印:

Have change with indexPath of: Optional([0, 0]), new index path of: Optional([0, 0]) for object: *my core data object*
update

来自苹果的documentation

Batch deletes run faster than deleting the Core Data entities yourself in code because they operate in the persistent store itself, at the SQL level. As part of this difference, the changes enacted on the persistent store are not reflected in the objects that are currently in memory.

After a batch delete has been executed, remove any objects in memory that have been deleted from the persistent store.

请参阅 Updating Your Application After Execution 部分以了解如何处理 NSBatchDeleteRequest 删除的对象。

这是示例,适合我。但是我没有找到如何用动画更新删除,因为 NSFetchedResultsControllerDelegate 没有触发。

@IBAction func didPressDelete(_ sender: UIBarButtonItem) {
        let managedObjectContext = persistentContainer.viewContext
        let request = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: Record.self))
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)

        do {
            // delete on persistance store level
            try managedObjectContext.execute(deleteRequest)

            // reload the whole context
            managedObjectContext.reset()
            try self.fetchedResultsController.performFetch()
            tableV.reloadData()
        } catch {
            print("")
        }
    }

Thanks.

由于 NSBatchDeleteRequest 在 persistentStore 级别(在磁盘上)删除托管对象,更新当前上下文 (in-memory) 的最有效方法是调用 refreshAllObjects()像这样的上下文:

func batchDelete() {
    if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
        let context = appDelegate.persistentContainer.viewContext
        if let request = self.fetchedResultsController.fetchRequest as? NSFetchRequest<NSFetchRequestResult> {
            let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: request)
            do {
                try context.execute(batchDeleteRequest) // performs the deletion
                appDelegate.saveContext()
                context.refreshAllObjects() // updates the fetchedResultsController
            } catch {
                print("Batch deletion failed.")
            }
        }
    }
}

无需重新获取所有对象或 tableView.reloadData()!