核心数据,在后台线程中修改NSManagedObject

时间:2015-02-20 06:39:11

标签: multithreading cocoa core-data nsmanagedobject nsmanagedobjectcontext

我想知道在主线程中加载的后台线程中修改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:错误:严重的应用程序错误。抓住了例外   在核心数据更改处理期间。这通常是一个bug   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()
  }
}

1 个答案:

答案 0 :(得分:0)

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

根据我们使用Core Data的经验,使用类型为MainQueueConcurrencyType的托管对象上下文和PrivateQueueConcurrencyType performBlockperformBlockAndWait的托管对象上下文比使用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()
    }
}