30

我有一个具有两个托管对象上下文设置的应用程序,如下所示:

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

将新的托管对象插入主上下文时,我保存主上下文,然后保存父上下文,如下所示:

[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] );
    }
}];

我的理解是,首次创建管理对象时,它有一个临时的objectID. 然后保存主上下文,该对象及其临时 ID 进入父上下文。然后保存父上下文。保存最后一个上下文objectID时,父上下文中的临时变量将转换为永久变量objectID

所以:

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

    “CoreData 无法为 ...”完成故障。

这种方法有什么问题?

4

3 回答 3

43

这是一个已知的错误,希望尽快修复,但一般来说,获得一个永久 ID 就足够了,前提是您在将数据保存到第一个孩子之前这样做,并且您只包括插入的对象:

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

在某些复杂的情况下,最好在创建实例后立即获取永久 ID,尤其是在您有复杂关系的情况下。

你怎么打电话?什么时候打电话obtainPermanentIDsForObjects

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

于 2012-08-16T23:39:37.287 回答
11

正如上面乔迪所说,当使用子 ManagedObjectContext 在后台线程中创建新的 NSManagedObject 时,您必须在保存之前通过执行以下操作来强制创建永久 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];
于 2013-10-08T12:01:16.233 回答
2

Swift 中的 iOS 8.3 解决方案中仍然存在问题:

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
于 2015-04-22T20:47:06.500 回答