无法在具有现有ObjectWithID 的子上下文中检索临时对象:错误:

Posted

技术标签:

【中文标题】无法在具有现有ObjectWithID 的子上下文中检索临时对象:错误:【英文标题】:Cannot retrieve temporary object in child context with existingObjectWithID:error: 【发布时间】:2014-03-19 16:08:58 【问题描述】:

我在托管对象上下文中创建了一个对象,但尚未保存。此外,我创建了一个子上下文并希望在此上下文中修改此对象。为了能够修改这个对象,我必须在子上下文中检索这个临时对象。

我使用 MagicalRecord 的方法MR_inContext:,它使用方法existingObjectWithID:error:。但这不会返回给定临时对象 ID 的对象。 objectWithID: 的方法似乎确实有效。我不明白为什么existingObjectWithID:error: 不起作用但objectWithID: 在这种情况下起作用。

- (void)testTemporaryObjectInChildContext 
    NSURL *modelURL = [[NSBundle bundleForClass:SSDProject.class] URLForResource:@"SSDProject" withExtension:@"momd"];
    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

    NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    [coordinator MR_addInMemoryStore];

    NSManagedObjectContext *mainContext = [NSManagedObjectContext MR_mainQueueContext];
    mainContext.persistentStoreCoordinator = coordinator;

    SSDProject *project = [SSDProject MR_createInContext:mainContext];

    NSManagedObjectContext *childContext = [NSManagedObjectContext MR_confinementContextWithParent:mainContext];

    // Fails
    NSError *error = nil;
    XCTAssertNotNil([childContext existingObjectWithID:project.objectID error:&error], @"");
    NSLog(@"Error: %@", error);

    // Succeeds
    XCTAssertNotNil([childContext objectWithID:project.objectID], @"");

【问题讨论】:

【参考方案1】:

existingObjectWithID:error: 在此处返回 nil 的原因仅仅是该对象存在于该上下文中,因为它是来自不同上下文的临时对象。

如果您在一个托管对象上下文中创建一个对象但不保存它,则它只存在于该上下文中。没有其他托管对象上下文有任何方式知道它。它不在他们的内存中,并且由于它不在持久存储中,因此他们无法找到它。当您调用existingObjectWithID:error: 时,上下文会检查它在内存中的内容,并在必要时检查持久存储。对于来自不同上下文的未保存对象,检查都不会找到任何东西。

保存更改后,该对象将写入持久存储文件,然后其他上下文可以找到它。此时,existingObjectWithID:error: 将返回非nil 作为 ID。

调用objectWithID: 将返回一个对象,但不是您可以使用的对象。该方法假定您提供了有效的对象 ID,因此它不会检查持久存储。如果对象存在,则其数据将正常故障输入。如果没有,你会得到一个例外。当您知道对象存在并想要更快的响应时,此方法很有用。在您的情况下,该对象不存在,因此调用此方法会给您一个定时炸弹,会炸毁您的应用程序。

【讨论】:

我一直认为子上下文在从存储协调器获取对象之前从其父上下文获取对象,即使对象尚未保存也是如此。好的,您对objectWithID:不测试对象是否存在于商店中的解释将解释existingObjectWithID:error:的差异。所以唯一的解决方案是获取PermanentIDsForObjects:error: ?这有什么缺点吗? 缺点是它不是一个解决方案。当然,您将获得一个永久 ID,但该对象仍然不存在于持久存储中,因此其他上下文仍然无法找到它。您确实需要一个永久 ID,但仅靠它是不够的。另一个上下文必须有某种定位对象的方法。 对象不应该对其他上下文可见,只对子上下文可见。在不保存父上下文的情况下使其在子上下文中可定位的唯一方法是使用 gainPermanentIDsForObjects:error: 不幸。 一个子上下文另一个上下文。 我有一个子上下文(主队列)保存到父(私有队列)中,然后另一个子,它共享同一个父,使用它的 objectID 检索对象。我假设这会起作用,因为您阅读的所有内容都表明父上下文被视为子项的永久存储,但发现,不,我必须将父上下文保存到(真正的)永久存储中才能做到这一点去工作。 objectID 是临时的还是永久的似乎并不重要。【参考方案2】:

objectWithID: 总是返回一个对象,但它可能无效(并且在您尝试使用它时可能会引发异常)。 existingObjectWithID:error: 在返回之前检查是否存在,如果找不到对象则返回 nil。所以,使用existingObjectWithID:error: 是安全的选择。

您应该先保存新对象,然后再尝试在另一个上下文中访问它,或者干脆不使用另一个上下文(不清楚为什么要执行您正在尝试执行的操作...)。

【讨论】:

我使用多个上下文进行分阶段更改,以便我可以丢弃每个子上下文及其更改。不幸的是,我无法保存以前的上下文。我也不想使用 objectWithID:,因为我不能完全确定该对象始终存在。 那么,如果您不打算保存对象,为什么还要在父上下文中创建它呢? 简化,用户可以取消进程并放弃所有更改。 我仍然不明白为什么这会阻止在子上下文中创建它。这就是你需要做的......

以上是关于无法在具有现有ObjectWithID 的子上下文中检索临时对象:错误:的主要内容,如果未能解决你的问题,请参考以下文章

核心数据:获取背景并在主线程上使用 objectWithID,性能优势?

元素“entityFramework”具有无效的子元素“提供者”。预期的可能元素列表:“上下文”

这是文档中的错误吗? -existingObjectWithID:error: 或 -objectWithID: 似乎声称不同的东西不匹配

具有实体框架 6 的 ObjectContext 在现有相关实体上插入重复项

无法将私有队列中的上下文设置为主队列中另一个上下文的子级

NSManagedObjectContext objectWithID 生命周期方法(派生属性)