你如何在块之间访问 NSManagedObject?

How do you access NSManagedObjects between blocks?

正如标题所说,如何访问在一个块中创建然后需要在另一个块中访问的 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];

}];

解决方案

此答案和评论导致以下解决方案。 在保存 object 时将临时 ID 分配给它,因此在尝试使用 TempID 获取 object 时会发生异常。

与其创建一个全新的获取请求,还不如要求上下文尽早获取永久 ID,而不是获取 object 的永久 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];

}];

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

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

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

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

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

这将从具有该 ID 的商店中获取对象,如果不存在,则 return 为 nil。与 objectWithID 不同,对象不会出错;将检索其所有属性。

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

有关 objectID 的更多详细信息,请参阅 Core Data Programming Guide

根据用户提问编辑

这个答案和评论导致了正确的解决方案。 在保存对象时将临时 ID 分配给对象,因此在尝试使用 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];

}];

您应该在您的块外访问 objectID(例如,在主线程上),然后在您的块内使用它。类似于:

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