保存子上下文时获取 NSFetchedResultsController 的批量更新

Posted

技术标签:

【中文标题】保存子上下文时获取 NSFetchedResultsController 的批量更新【英文标题】:Get batch update for NSFetchedResultsController when child contexts are saved 【发布时间】:2016-03-07 16:08:17 【问题描述】:

我有两个私有 NSManagedObject 上下文,它们在后台线程上处理异步更新核心数据实体,在这些上下文更新后,它们保存到主线程上的主上下文中。那时,如果有很多更新,我的主要上下文和 NSFetchedResultsController 就会受到更新的冲击。有没有办法批量做到这一点?

我曾尝试发送通知、使用委托等,其中数据在子上下文中更新并且它们按预期工作,但在主上下文保存操作发生之前全部在后台线程上触发。

我认为理想情况下我想在“大”保存正在进行时分离委托,在完成时以某种方式得到通知,执行异步获取请求并在请求完成时调用表上的 reloadData。

我有一个 UITableView 和一个 NSFetchedResultsController 使用 UIViewController 上的主上下文:

let fetchRequest = NSFetchRequest(entityName: Message)

fetchRequest.predicate  = NSPredicate(format: "groupId == %@", self.groupId)

let idSort          = NSSortDescriptor(key: "id", ascending: true)
fetchRequest.sortDescriptors = [idSort]

self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.coreDataStack.context, sectionNameKeyPath: nil, cacheName: nil)

do 

try self.fetchedResultsController.performFetch()

 catch 
print("error fetch")


self.fetchedResultsController.delegate = self

然后我使用 NSFetchedResultsController 提供的委托:

func controllerWillChangeContent(controller: NSFetchedResultsController) 

    dispatch_async(dispatch_get_main_queue())  () -> Void in

        self.tableView?.beginUpdates()
    


func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) 

    dispatch_async(dispatch_get_main_queue())  () -> Void in

        switch type 

        case .Insert:

            self.tableView?.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)
            self.newIndexPath = newIndexPath

        case .Delete:
            self.tableView?.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)

        case .Update:
            return

        case .Move:
            self.tableView?.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
            self.tableView?.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)

        
    


func controllerDidChangeContent(controller: NSFetchedResultsController) 

    dispatch_async(dispatch_get_main_queue())  () -> Void in

        self.tableView?.endUpdates()

        // Scroll the table view
        if self.newIndexPath != nil 
            self.messagesTable?.scrollToRowAtIndexPath(self.newIndexPath!, atScrollPosition: .Bottom, animated: false)
            self.newIndexPath = nil
        
    


func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) 

    let indexSet = NSIndexSet(index: sectionIndex)

    switch type 

    case .Insert:
        self.tableView!.insertSections(indexSet, withRowAnimation: .Automatic)

    case .Delete:
        self.tableView!.deleteSections(indexSet, withRowAnimation: .Automatic)

    default:
        break

    

【问题讨论】:

【参考方案1】:

考虑更改您的上下文配置。您可以直接在持久存储协调器之上创建背景上下文,而不是创建主队列上下文的子上下文。主要上下文也是如此。 现在,当您导入数据并保存在后台上下文中时,您的数据将通过 psc 写入 sql 层。在您之前的设置中,后台上下文将保存到它们的父级,这是主上下文。 为了将更改从后台上下文传播到主上下文,请侦听 nsmanagedobjectcontextdidsave 通知。然后,您可以将后台上下文中的更改合并到主上下文中。这将减少税收。但是,取决于获取请求。确保尽可能优化这一点。

查看文档:https://developer.apple.com/library/ios/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/

祝你好运

【讨论】:

我真的很喜欢这个主意。在实现它之前还需要一点时间 - 我设法通过其他委托调用更改结果控制器的委托分配来解决我的问题,但这听起来更干净/更快。

以上是关于保存子上下文时获取 NSFetchedResultsController 的批量更新的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 中执行子获取时,在父上下文中修改托管对象是不是会向下传播到子上下文?

与核心数据的关系错误

子父上下文保存冲突

核心数据父/子上下文保存失败

当保存在后台异步完成时,我应该如何保证从嵌套上下文中的不同线程获取结果是最新的?

在子上下文中保存核心数据不起作用