使用 performBackgroundTask 更新 NSFetchedResultsController [英] Update NSFetchedResultsController using performBackgroundTask

查看:22
本文介绍了使用 performBackgroundTask 更新 NSFetchedResultsController的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 NSFetchedResultsController 并且我正在尝试在后台上下文中更新我的数据.例如,这里我试图删除一个对象:

I have an NSFetchedResultsController and I am trying to update my data on a background context. For example, here I am trying to delete an object:

persistentContainer.performBackgroundTask { context in
  let object = context.object(with: restaurant.objectID)
  context.delete(object)
  try? context.save()
}

有两件事我不明白:

  1. 我希望这会修改,但不保存父上下文.但是,肯定会保存父上下文(通过手动打开 SQLite 文件进行验证).
  2. 我本希望 NSFetchedResultsController 在后台内容保存回其父级时更新,但这并没有发生.我需要在主线程上手动触发某些东西吗?
  1. I would have expected this to modify, but not save the parent context. However, the parent context is definitely being saved (as verified by manually opening the SQLite file).
  2. I would have expected the NSFetchedResultsController to update when the background content saves back up to its parent, but this is not happening. Do I need to manually trigger something on the main thread?

显然有些东西我没有得到.有人能解释一下吗?

Obviously there is something I am not getting. Can anybody explain this?

我知道我已经正确实现了获取的结果控制器委托方法,因为如果我更改代码以直接更新 viewContext,一切都会按预期进行.

I know that I have implemented the fetched results controller delegate methods correctly, because if I change my code to directly update the viewContext, everything works as expected.

推荐答案

说明

NSPersistentContainer 的实例方法 performBackgroundTask(_:)newBackgroundContext() 没有很好的文档记录.

Explanation

NSPersistentContainer's instance methods performBackgroundTask(_:) and newBackgroundContext() are poorly documented.

无论您调用哪种方法,在任何一种情况下,(返回的)临时 NSManagedObjectContext 都使用 privateQueueConcurrencyType 设置并与 NSPersistentStoreCoordinator 相关联> 直接,因此没有 parent.

No matter which method you call, in either case the (returned) temporary NSManagedObjectContext is set up with privateQueueConcurrencyType and is associated with the NSPersistentStoreCoordinator directly and therefore has no parent.

请参阅文档:

调用这个方法会导致持久化容器创建和返回一个新的 NSManagedObjectContext,concurrencyType 设置为私有队列并发类型.这个新的上下文将与NSPersistentStoreCoordinator 直接并设置为消费NSManagedObjectContextDidSave 自动广播.

Invoking this method causes the persistent container to create and return a new NSManagedObjectContext with the concurrencyType set to privateQueueConcurrencyType. This new context will be associated with the NSPersistentStoreCoordinator directly and is set to consume NSManagedObjectContextDidSave broadcasts automatically.

...或自己确认:

persistentContainer.performBackgroundTask { (context) in
    print(context.parent) // nil
    print(context.persistentStoreCoordinator) // Optional(<NSPersistentStoreCoordinator: 0x...>)
}

let context = persistentContainer.newBackgroundContext()
print(context.parent) // nil
print(context.persistentStoreCoordinator) // Optional(<NSPersistentStoreCoordinator: 0x...>)

由于缺少 parent,更改不会提交到 parent context,例如viewContextviewContext 未受影响,连接的 NSFetchedResultsController 不会识别任何更改,因此不会更新或调用它的 委托的方法.相反,更改将直接推送到 persistent store coordinator,然后保存到 persistent store.

Due to the lack of a parent, changes won't get committed to a parent context like e.g. the viewContext and with the viewContext untouched, a connected NSFetchedResultsController won’t recognize any changes and therefore doesn’t update or call its delegate's methods. Instead changes will be pushed directly to the persistent store coordinator and after that saved to the persistent store.

我希望我能够帮助您,如果您需要进一步的帮助,我可以在我的回答中添加,如您所描述的,如何获得所需的行为.(解决方案在下面添加)

I hope, that I was able to help you and if you need further assistance, I can add, how to get the desired behavior, as described by you, to my answer. (Solution added below)

您通过使用两个具有父子关系的 NSManagedObjectContext 来实现您所描述的行为:

You achieve the behavior, as described by you, by using two NSManagedObjectContexts with a parent-child relationship:

// Create new context for asynchronous execution with privateQueueConcurrencyType  
let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
// Add your viewContext as parent, therefore changes are pushed to the viewContext, instead of the persistent store coordinator
let viewContext = persistentContainer.viewContext
backgroundContext.parent = viewContext
backgroundContext.perform {
    // Do your work...
    let object = backgroundContext.object(with: restaurant.objectID)
    backgroundContext.delete(object)
    // Propagate changes to the viewContext -> fetched results controller will be notified as a consequence
    try? backgroundContext.save()
    viewContext.performAndWait {
        // Save viewContext on the main queue in order to store changes persistently
        try? viewContext.save()
    }
}

但是,您也可以坚持使用 performBackgroundTask(_:) 或使用 newBackgroundContext().但如前所述,在这种情况下,更改会直接保存到持久存储中,并且默认情况下不会更新 viewContext.为了将更改向下传播到 viewContext,从而通知 NSFetchedResultsController,您必须设置 viewContext.automaticallyMergesChangesFromParent到<代码>真:

However, you can also stick with performBackgroundTask(_:) or use newBackgroundContext(). But as said before, in this case changes are saved to the persistent store directly and the viewContext isn't updated by default. In order to propagate changes down to the viewContext, which causes NSFetchedResultsController to be notified, you have to set viewContext.automaticallyMergesChangesFromParent to true:

// Set automaticallyMergesChangesFromParent to true
persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
persistentContainer.performBackgroundTask { context in
    // Do your work...
    let object = context.object(with: restaurant.objectID)
    context.delete(object)
    // Save changes to persistent store, update viewContext and notify fetched results controller
    try? context.save()
}

请注意,大量更改(例如一次添加 10.000 个对象)可能会使您的 NSFetchedResultsController 发疯,从而阻塞 主队列.

Please note that extensive changes such as adding 10.000 objects at once will likely drive your NSFetchedResultsController mad and therefore block the main queue.

这篇关于使用 performBackgroundTask 更新 NSFetchedResultsController的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆