使用带有 NSBatchDeleteRequest 的 NSFetchedResultsController

Posted

技术标签:

【中文标题】使用带有 NSBatchDeleteRequest 的 NSFetchedResultsController【英文标题】:Using NSFetchedResultsController w/NSBatchDeleteRequest 【发布时间】:2016-11-13 15:11:29 【问题描述】:

我在使用 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", $0))
    

    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

【问题讨论】:

您正在父上下文上执行删除,并且您说要使用提供的私有上下文进行删除。 @ELKA 获取的结果控制器是使用父上下文创建的,父上下文也是上下文,其父上下文是应该删除记录的持久存储。 因此 context.parent 将是一个主要的 NSManagedObjectContext ,它与 NSFetchedResultController 的上下文相同。对吗? @ELKA 是的,就是这样。 你可以试试context.parent?.processPendingChanges()而不是refreshAllObjects() 【参考方案1】:

来自苹果的documentation:

批量删除比您自己在代码中删除核心数据实体运行得更快,因为它们在 SQL 级别的持久存储本身中运行。作为这种差异的一部分,对持久存储进行的更改不会反映在当前内存中的对象中。

执行批量删除后,删除内存中已从持久存储中删除的所有对象。

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

【讨论】:

单独的文档不起作用,但在删除请求中将结果类型更新为对象 ID,然后在获取的结果控制器使用的上下文中调用刷新所有对象,使更改以删除的形式出现! 文档没有明确显示代码,但它确实声明:“要做到这一点,首先确保 NSBatchDeleteRequestresultType 设置为 NSBatchDeleteRequestResultType.resultTypeObjectIDs 之前请求被执行。”所以一定要包括:deleteRequest.resultType = .resultTypeObjectIDs. 非常感谢,这正是我想要的!【参考方案2】:

这是示例,对我有用。但我没有找到如何使用动画更新删除,因为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.

【讨论】:

【参考方案3】:

由于NSBatchDeleteRequest 删除了persistentStore 级别的托管对象(在磁盘上),因此更新当前上下文(在内存中)的最有效方法是在上下文中调用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()

【讨论】:

我认为应遵循已接受答案中链接的文档,我的原始评论/问题已刷新所有对象,但这些天的方法是将更改合并到目标上下文中(s ) 来自删除请求的结果。

以上是关于使用带有 NSBatchDeleteRequest 的 NSFetchedResultsController的主要内容,如果未能解决你的问题,请参考以下文章

NSBatchDeleteRequest 留下不好的关系

核心数据 NSBatchDeleteRequest 似乎将对象留在上下文中

如何设置 NSBatchDeleteRequest 的 NSBatchDeleteResult 类型?

UI 在执行 NSBatchDeleteRequest 时冻结

如何让 NSBatchDeleteRequest 删除并允许您再次添加回对象

使用带有 uuencode 的“sendmail”发送邮件,并带有主题