如何在块之间访问 NSManagedObject?

Posted

技术标签:

【中文标题】如何在块之间访问 NSManagedObject?【英文标题】:How do you access NSManagedObjects between blocks? 【发布时间】:2015-04-26 05:30:24 【问题描述】:

正如标题所说,如何访问已在一个块中创建然后需要在另一个块中访问的 NSManagedObject。我有以下实现,想知道它是否正确。

__block Person *newPerson;

@weakify(self);
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 

    newPerson = [Person MR_createInContext:localContext];
    newPerson.name = @"Bob";

 completion:^(BOOL success, NSError *error) 
    @strongify(self);
    // Called on main thread

    PersonViewController *personVC = [[PersonViewController alloc] initWithPerson:newPerson];
    [self.navigationController pushViewController:personVC animated:YES];

];

我不需要从完成处理程序中的 localContext 访问 newPerson 是否正确,因为它将在主线程上执行?

编辑

建议的方式如下:

__block NSManagedObjectID *newPersonObjectID;

@weakify(self);
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 

    Person *newPerson = [Person MR_createInContext:localContext];
    newPerson.name = @"Bob";
    newPersonObjectID = newPerson.objectID;

 completion:^(BOOL success, NSError *error) 
    @strongify(self);
    // Called on main thread

    Person *savedPerson = [[NSManagedObjectContext MR_defaultContext] objectWithID:newPersonObjectID];

    PersonViewController *personVC = [[PersonViewController alloc] initWithPerson:savedPerson];
    [self.navigationController pushViewController:personVC animated:YES];

];

解决方案

这个答案和 cmets 导致以下解决方案。 一个 TemporaryID 在保存时被分配给对象,因此在尝试使用 TempID 获取对象时会发生异常。

与其创建一个全新的获取请求,不如让上下文提前获取永久 ID,而不是获取对象的永久 ID。例如:

__block NSManagedObjectID *newPersonObjectID;

@weakify(self);
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 

    Person *newPerson = [Person MR_createInContext:localContext];
    newPerson.name = @"Bob";

    [localContext obtainPermanentIDsForObjects:@[newPerson] error:NULL];

    newPersonObjectID = newPerson.objectID;

 completion:^(BOOL success, NSError *error) 
    @strongify(self);
    // Called on main thread

    Person *savedPerson = [[NSManagedObjectContext MR_defaultContext] objectWithID:newPersonObjectID];

    PersonViewController *personVC = [[PersonViewController alloc] initWithPerson:savedPerson];
    [self.navigationController pushViewController:personVC animated:YES];

];

【问题讨论】:

我们回答了您的问题吗?如果社区仍然可以为您提供任何具体的帮助,您可以编辑您的问题! 【参考方案1】:

您不能直接在上下文之间传递托管对象。每个NSManagedObject 只能通过其自己的上下文访问。

您需要将其objectID 传递给完成块,然后通过调用以下方法之一让主上下文获取对象:

-(NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID

这将为具有指定 objectID 的对象创建故障,无论它是否实际存在于存储中。如果它不存在,任何触发错误的东西都会失败并抛出异常。

-(NSManagedObject *)existingObjectWithID:(NSManagedObjectID *)objectID
                                   error:(NSError **)error

这将从具有该 ID 的商店中获取对象,如果不存在则返回 nil。不像objectWithID,对象不会出错;它的所有属性都将被检索到。

在任何一种情况下,您的本地上下文都必须将 Person 对象保存到存储中,以便主上下文能够获取它。

更多关于objectID的详细信息可以在Core Data Programming Guide中找到

用户提问编辑

这个答案和 cmets 导致正确的解决方案。 一个 TemporaryID 在保存时被分配给对象,因此在尝试使用 TempID 获取对象时会发生异常。

与其创建一个全新的获取请求,不如让上下文提前获取永久 ID,而不是获取对象的永久 ID。例如:

__block NSManagedObjectID *newPersonObjectID;

@weakify(self);
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 

    Person *newPerson = [Person MR_createInContext:localContext];
    newPerson.name = @"Bob";

    [localContext obtainPermanentIDsForObjects:@[newPerson] error:NULL];

    newPersonObjectID = newPerson.objectID;

 completion:^(BOOL success, NSError *error) 
    @strongify(self);
    // Called on main thread

    Person *savedPerson = [[NSManagedObjectContext MR_defaultContext] objectWithID:newPersonObjectID];

    PersonViewController *personVC = [[PersonViewController alloc] initWithPerson:savedPerson];
    [self.navigationController pushViewController:personVC animated:YES];

];

【讨论】:

感谢您的回复。我已经更新了代码。你能告诉我这是否是你的意思吗? 是的,我就是这个意思。我唯一要补充的是,在保存对象之前,它有一个临时对象 ID。保存后,它会被赋予一个永久的 objectID。我不使用 MagicalRecord,所以我不知道您是否必须先获取永久 ID,然后才能尝试在完成块中获取托管对象。我认为 objectWithID 可能需要一个永久 ID,但 existingObjectWithID 可能需要一个临时 ID。 啊啊啊就是这样!我没有意识到这是儿童上下文中的 TempID,谢谢!在使用 objectWithID 的完成块中导致异常,因为显然我有 TempID。我会看看 existingObjectWithID 是否可以从 TempID 返回一个对象,如果不是,我可能需要获取该对象。 好吧,所以它没有,但我发现了这个奇妙的答案,您可以在其中强制在保存块中创建永久 ID。 ***.com/a/14115769/4698501 感谢您帮助我走到这一步!我会尝试编辑您的答案,使其与我发现的内容更相关并接受它。【参考方案2】:

您应该在块之外(例如在主线程上)访问 objectID,然后在块中使用它。比如:

NSManagedObjectID *objectID = appDelegate.myObject.objectId;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
    // use objectID here.

【讨论】:

以上是关于如何在块之间访问 NSManagedObject?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NSManagedObject 对象在视图控制器之间传递自定义属性?

如何使用尚未保存在 iOS 中的 objectID 访问 NSManagedObject 实体?

如何在 coredata/NSManagedObject 模型数据更改与应用程序用户界面之间进行紧密耦合?

如何以可在绑定中访问的可重用方式修改 NSManagedObject 类的行为?

在块内,__block 变量和静态变量之间的实际区别是啥?

在 ViewController 之间传递 NSManagedObject