核心数据:儿童上下文是否获得新插入对象的永久对象ID?

我有一个应用程序与两个pipe理对象上下文设置像这样:

  • 父上下文:链接到持久性存储的NSPrivateQueueConcurrencyType。
  • 主要上下文:NSMainQueueConcurrencyType,父上下文的子项。

当插入一个新的托pipe对象到主要的上下文,我保存主要的上下文,然后像这样的父上下文:

[context performBlockAndWait:^{ NSError * error = nil; if (![context save: &error]) { NSLog(@"Core Data save error %@, %@", error, [error userInfo]); } }]; [parentContext performBlock:^{ NSError *error = nil; BOOL result = [parentContext save: &error]; if ( ! result ) { NSLog( @"Core Data save error in parent context %@, %@", error, [error userInfo] ); } }]; 

我的理解是,当pipe理对象第一次创build时,它有一个临时的objectID 。 然后主要上下文被保存,并且这个对象和它的临时ID被获取到父上下文。 然后父上下文被保存。 当最后一个上下文被保存时,父上下文中的临时objectID被转换成永久的objectID

所以:

  • 永久对象ID是否会自动传回主(子)上下文?
  • 当我强制使用[NSManagedObjectContext obtainPermanentIDsForObjects:error:]获取对象永久ID,然后背景应用程序,重新激活它,重新加载,获取对象使用主要上下文的objectWithID:并访问一个属性,我得到

“CoreData无法完成……的错误”。 这种方法有什么问题?

这是一个已知的bug,希望尽快修复,但通常情况下,获得一个永久ID是足够的,只要你在第一个孩子保存数据之前这样做,你只包括插入的对象:

 [moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:&error] 

在一些复杂情况下,最好在创build实例时获得永久ID,特别是在复杂关系的情况下。

你怎样和何时调用obtainPermanentIDsForObjects

我不关注应用程序崩溃的部分。 也许更好的解释会有所帮助。

正如Jody上面所说的,当使用子ManagedObjectContext在后台线程中创build一个新的NSManagedObject时,您必须在保存之前通过执行以下操作来强制创build一个永久ID:

 NSError *error = nil; [threadedMOC obtainPermanentIDsForObjects:threadedMOC.insertedObjects.allObjects error:&error]; BOOL success = [threadedMOC save:&error]; 

恕我直言,这样做并不是很直观 – 毕竟,在您保存之前,您需要一个永久ID。 但这似乎是工作的方式。 如果您在保存之后询问永久ID,则ID仍然是暂时的。 从Apple Docs中,您实际上可以使用以下来确定对象的ID是否是临时的:

 BOOL isTemporary = [[managedObject objectID] isTemporaryID]; 

Swift中iOS 8.3解决scheme中依然存在问题:

 func saveContext(context: NSManagedObjectContext?){ NSOperationQueue.mainQueue().addOperationWithBlock(){ if let moc = context { var error : NSError? = nil if !moc.obtainPermanentIDsForObjects(Array(moc.insertedObjects), error: &error){ println("\(__FUNCTION__)\n \(error?.localizedDescription)\n \(error?.userInfo)") } if moc.hasChanges && !moc.save(&error){ println("\(__FUNCTION__)\n \(error?.localizedDescription)\n \(error?.userInfo)") } } } } func saveBackgroundContext(){ saveContext(self.defaultContext) privateContext?.performBlock{ var error : NSError? = nil if let context = self.privateContext { if context.hasChanges && !context.save(&error){ println("\(__FUNCTION__)\n \(error?.localizedDescription)\n \(error?.userInfo)") }else { println("saved private context to disk") } } } } 

哪里:

  • defaultContext具有concurrencyType .MainQueueConcurrencyType
  • privateContext具有concurrencyType .PrivateQueueConcurrencyType