正确实现父/子NSManagedObjectContext

Ben*_*ard 27 cocoa core-data objective-c nsmanagedobjectcontext ios

我的应用程序有时会将对象插入到托管对象上下文中,而这些对象并不一定要保存.例如,当我启动"添加实体"模式时,我创建了一个托管对象并将其分配给模态.如果用户从该模态中保存,我将保存上下文.如果他取消,我删除该对象,不需要保存.

我现在已经引入了一个"导入"功能,切换到我的应用程序(使用URL方案)并添加一个实体.由于其中一个模态可能是打开的,因此此时保存上下文是不安全的.即使用户取消,也会保存为模态创建的瞬态对象,并且无法保证稍后将保存删除(来自取消操作) - 用户可能会退出该应用程序.

同样,每当我的应用程序退出时,我都无法保存.如果模式在该点处打开,则将错误地保存临时对象.

为了解决这个问题,我试图用一个孩子背景下,作为讨论在这里.阅读了所有我能在SO上找到的东西,我会有几个问题:

  1. 我应该为每个上下文使用哪种并发类型?请记住,我没有这样做是为了获得性能/线程优势.我知道如果要有子上下文,我不能使用NSConfinementConcurrencyType作为主上下文,但我不确定其他两个选项中哪一个最适合.对于子上下文,它是否需要匹配?或者我甚至可以在这里使用限制类型?我尝试了各种各样的组合,似乎一切正常,但我想知道哪个适合我的要求.

  2. (附带问题)如果我使用iVar类,为什么我才能让它工作?我以为我应该能够在创建它的方法中声明临时上下文,然后使用entity.managedObjectContext来引用它.但是当我访问它时它似乎是零?如果我改为使用iVar来保存引用,则会对此进行纠正.

  3. 什么是正确的方法或将更改传播到主要上下文?我已经在每个上下文中看到了使用不同的块包装实现的各种注释.它取决于我的并发类型吗?我目前的版本是:

    //save the new entity in the temporary context
    NSError *error = nil;
    if (![myObject.managedObjectContext save:&error]) {NSLog(@"Error - unable to save new object in its (temporary) context");}
    
    //propogate the save to the main context
    [self.mainContext performBlock:^{
        NSError *error2 = nil;
        if (![self.mainContext save:&error2]) {NSLog(@"Error - unable to merge new entity into main context");}
    }];
    
    Run Code Online (Sandbox Code Playgroud)
  4. 当我的用户保存时,它会向其委托(我的主视图控制器)发送一条消息.委托传递添加的对象,它必须在主上下文中找到相同的对象.但是当我在主要环境中寻找它时,却找不到它.主要上下文确实包含实体 - 我可以记录它的详细信息并确认它在那里 - 但地址是不同的?如果这意味着发生(为什么?),如何在保存后在主上下文中找到添加的对象?

感谢您的任何见解.很抱歉有一个很长的,多部分的问题,但我认为有人可能已经解决过所有这些问题.

jms*_*617 47

父/子MOC模型是核心数据的一个非常强大的功能.它简化了我们以前必须处理的古老并发问题.但是,正如您所说,并发不是您的问题.回答你的问题:

  1. 传统上,您使用NSMainQueueConcurrencyTypefor NSManagedObjectContext与主线程关联,NSPrivateQueueConcurrencyTypes用于子上下文.子上下文不需要与其父上下文匹配.该NSConfinementConcurrencyType是所有NSManagedObjectContextS ^得到默认的,如果你不指定类型.它基本上是"我将管理我自己的Core Data线程"类型.
  2. 在没有看到您的代码的情况下,我的假设是您创建子上下文的范围结束并且它被清理.
  3. 使用父/子上下文模式时,您需要使用块方法.使用块方法的最大好处是操作系统将处理将方法调用分派给正确的线程.您可以performBlock用于异步执行或performBlockAndWait同步执行.

你会使用这样的:

- (void)saveContexts {
    [childContext performBlock:^{
        NSError *childError = nil;
        if ([childContext save:&childError]) {
            [parentContext performBlock:^{
                NSError *parentError = nil;
                if (![parentContext save:&parentError]) {
                    NSLog(@"Error saving parent");
                }
            }];
        } else {
            NSLog(@"Error saving child");
        }
    }];
}
Run Code Online (Sandbox Code Playgroud)

现在,您需要记住,在保存之前,子上下文中所做的更改(例如插入的实体)将不可用于父上下文.对于子上下文,父上下文是持久性存储.保存时,将这些更改传递给父级,然后父级可以将它们保存到实际的持久性存储中.将propogate更改保存在一个级别.另一方面,获取子上下文会将数据向下拉到每个级别(通过父级和子级)

  1. 您需要objectWithID在managedObjectContext上使用某种形式.它们是在上下文之间传递对象的最安全(也是唯一的)方法.正如Tom Harrington在评论中提到的那样,你可能想要使用,existingObjectWithID:error:因为objectWithID:总是返回一个对象,即使你传入一个无效的ID(这可能导致例外).有关详细信息:链接

  • 一个很好的答案.我建议使用`existingObjectWithID:error:`来代替`objectWithID:` - 因为`objectWithID:`总是一个对象,即使你传入了一个无效的ID(可能导致异常).最好确保只获得已知存在的对象. (2认同)

And*_*ndy 6

  1. 如果使用父/子模式,通常会使用子上下文NSMainQueueConcurrencyType和子上下文声明父上下文NSPrivateQueueConcurrencyType.NSConfinementConcurrencyType用于经典线程模式.

  2. 如果你想保留上下文,你需要以某种方式强烈引用它.

  3. 您只需在子上下文中调用save方法将更改推送到父上下文,如果要保留数据,也可以在父上下文中调用save.你不需要在一个街区内做这件事.

  4. 有几种方法可以从上下文中获取特定对象.我不能告诉你哪一个会适用于你的情况,试试看:

    - objectRegisteredForID:

    - objectWithID:

    - existingObjectWithID:error:


Dev*_*ang 6

我遇到了类似的问题,这里是你问题的某些部分的答案 - 1.你应该能够使用并发类型NSPrivateQueueConcurrencyTypeNSMainQueueConcurrencyType 2.假设你已经创建了一个tempContext带有父上下文的临时上下文mainContext(假设是iOS5).在这种情况下,您可以将托管对象从- 移动tempContextmainContext-

object = (Object *)[mainContext objectWithID:object.objectID];
Run Code Online (Sandbox Code Playgroud)

然后,您可以保存mainContext本身.

也许,

[childContext reset];
Run Code Online (Sandbox Code Playgroud)

如果要重置临时上下文.