How do you access NSManagedObjects between blocks?

Just like the title, as it says about accessing an NSManagedObject that was created in one block and then need to access another. I have the following implementation and was wondering if this is correct.

__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];

}];

      

Am I correct in not needing access to newPerson from localContext in the completion handler because it will be executed on the main thread?

EDIT

It looks like the suggested way is:

__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];

}];

      

Decision

This answer and comments lead to the following solution. The temporary identifier is assigned to the object at the time it is saved, so an exception is thrown when trying to retrieve the object using TempID.

Instead of creating an entire new fetch request, which can be done, ask the context to get the early ids earlier, and then get the persistent object id. For example:

__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];

}];

      

+3


source to share


2 answers


You cannot directly pass managed objects between contexts. Each NSManagedObject

is only available in its own context.

You need to pass yours objectID

to the completion block, then include the main object context by calling one of the following methods:

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

      

This will throw an error for an object with the specified object id, regardless of whether it actually exists in the store. If it doesn't exist, whatever is causing the error will fail.

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

      

This will fetch the object from storage that has that identifier, or return zero if it doesn't exist. In contrast objectWithID

, the object will not crash; all of its attributes will be restored.



Either way, your local context had to store the object Person

in storage for the main context to retrieve it.

More information on objectID

can be found in the Master Data Programming Guide

Edit per user asked question

This answer and comments lead to the correct solution. The temporary identifier is assigned to the object at the time it is saved, so an exception is thrown when trying to retrieve the object using TempID.

Instead of creating an entire new fetch request, which can be done, ask the context to get the early ids earlier, and then get the persistent object id. For example:

__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];

}];

      

+2


source


You have to access the ID object outside of your block (for example, in the main thread) and then use it in your block. Something like:



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

      

+1


source







All Articles