Core Data,在后台线程中修改 NSManagedObject

Posted

技术标签:

【中文标题】Core Data,在后台线程中修改 NSManagedObject【英文标题】:Core Data, modifying NSManagedObject in background thread 【发布时间】:2015-02-20 06:39:10 【问题描述】:

我想知道是否可以在主线程中加载的后台线程中修改NSManagedObject - 然后将上下文保存回主线程中。我知道我无法从后台线程中保存上下文。

这主要是虚拟代码 (Swift),但它显​​示了我如何在后台线程中更改对象的属性并将上下文保存回主线程:

var myObject = coreDataHelper.loadSomeObjectFromDB()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) 
  doSomeHeavyLifting()
  myObject.someProperty = "foo"
  dispatch_async(dispatch_get_main_queue()) 
    coreDataHelper.saveManagedObjectContext()
  

我之所以问,是因为我的应用程序偶尔会出现与核心数据相关的随机崩溃,并且想知道这是否可能是由于我的上述工作流程造成的。

这是个例外:

CoreData:错误:严重的应用程序错误。异常被捕获 在核心数据更改处理期间。这通常是一个错误 NSManagedObjectContextObjectsDidChangeNotification 的观察者。 -[__NSCFSet addObject:]: 尝试用 userInfo (null) 插入 nil

只是我自己没有定义任何NSManagedObjectContextObjectsDidChangeNotification

如果这是导致崩溃的原因,是否像将行 myObject.someProperty = "foo" 移动到主队列中一样简单,就像这样?:

var myObject = coreDataHelper.loadSomeObjectFromDB()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) 
  doSomeHeavyLifting()
  dispatch_async(dispatch_get_main_queue()) 
    myObject.someProperty = "foo" //moved this line into main thread
    coreDataHelper.saveManagedObjectContext()
  

【问题讨论】:

这取决于上下文,如果您的 NSManagedContext 是在主线程上创建的,那么您只能在主线程上执行操作。 是的,上下文当然是在主线程上创建的。那就是我加载对象的地方。所以不仅不能在后台线程中保存上下文,而且不能在后台修改对象然后将上下文保存在主线程中以将这些更改保存到磁盘? 好吧,这也不是,您只能在同一个线程中操作 coredata 对象。尽管在 ios8 中,Apple 已经在 Coredata 中发布了异步获取。 code.tutsplus.com/tutorials/… 谢谢。这就是我所害怕的。这对我来说意味着更多的重构。我用另一种方法更新了这个问题。理论上可以吗,因为对象随后会在主线程中被操作回? 是的,应该可以。 【参考方案1】:

如 cmets 中所述(以一种或另一种方式),您不能将 NSManagedObject 的实例从一个线程传递到另一个线程。但是,您可以传递线程安全的NSManagedObjectID。然后,您可以使用context.objectWithID(objectID) 在不同的线程中检索对象。

根据我们使用 Core Data 的经验,使用 PrivateQueueConcurrencyTypeperformBlockperformBlockAndWait 类型的托管对象上下文比使用 ConfinementConcurrencyType 效果更好(默认) .因此,代码看起来类似于:

// in main thread
var myObject = getObjectInMainThread()
let objectID = myObject.objectID
let privateContext = coreDataHelper.createPrivateContext() // creates a PrivateConcurrencyType context and returns

privateContext.performBlock 
    var object = privateContext.objectWithID(objectID)
    doSomeHeavyLiftingWithObject()
    // coreDataHelper should be listening to 
    // NSManagedObjectContextDidSaveNotification and merge changes to main
    privateContext.save(nil) 
    dispatch_async(dispatch_get_main_queue()) 
        // because changes are merged by listening to context "did save"
        // notification, the update should already be reflected in the 
        // previously retrieved object
        myObject.someProperty = "foo" // moved this line into main thread
        coreDataHelper.saveManagedObjectContext()
    

【讨论】:

以上是关于Core Data,在后台线程中修改 NSManagedObject的主要内容,如果未能解决你的问题,请参考以下文章

Core Data 3 托管对象上下文

在后台线程上安全保存 Core Data 托管对象上下文的正确方法?

Core Data undo后台线程删除

如何使用 Core Data 有效地保存 UI/主线程中所做的更改?

Core Data 使用多个上下文中的新对象从后台线程订购一对多关系保存

Core-Data 后台保存性能问题