Search code examples
iosobjective-ccore-datansmanagedobjectcontextmagicalrecord

Finding NSManagedObject inside of NSOperation


I thought that [moc existingObjectWithID:self.objectID error:&error] was the right way to find an existing object inside of a thread, given its objectID, but I can't get it to work, why?

Background / Code

I've got an NSManagedObject which is already saved. I can see all its properties on the main thread. I'm passing the object to my NSOperation in a custom init method and in that method, I'm saving its objectID:

- (instancetype)initWithEntity:(NSManagedObject *)object
{
    self = [super init];
    if (self) {
        self.objectID = object.objectID;
        ...
    }
    return self;
}

Then in the main function of my operation I'm looking up my object so that I can use it.

- (void)main
{
    if (self.isCancelled) { return; }

    NSManagedObjectContext *moc = [NSManagedObjectContext MR_context];

    NSError *error = nil;
    self.object = [moc existingObjectWithID:self.objectID error:&error];

    if (error) {
        DDLogError(@"error finding object: %@", error);
    }

    ...
}

MR_context is a MagicalRecord method which simply sets up a new context with concurrency type NSPrivateQueueConcurrencyType with its parent set to the root saving context. All of that looks correct to me.

Errors

When no debugger is set, I simply get a fault for the object. If I try to look up any property on it, I get nil.

With the debugger turned on and -com.apple.CoreData.ConcurrencyDebug 1 set I get some errors:

In the simulator I get EXC_BAD_INSTRUCTION (code=EXC_i386_INVOP, subcode=0x0) on the [moc existingObjectWithID:self.objectID error:&error] line.

On the device, I get the following on that same line:

CoreData`+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]:
->  0x185946614 <+0>: brk    #0x1

So obviously something is wrong, but the objectID isn't temporary, it is the same objectID I see when inspecting the real object on the main thread. So I'm not sure why it isn't working.

I've also tried:

self.object = [moc objectWithID:self.objectID];

But that doesn't work either. Any ideas?

Updates

2015-07-10 - Ok, if I use a block with the moc then I can properly access the object from within that block.

[moc performBlock:^{
    NSError *error = nil;
    NSManagedObject *o = [moc existingObjectWithID:self.objectID error:&error];
}];

How do I properly extract that object so I can have access to it in the thread that the operation is running? Doing a __block property and then setting it in the block and using it out of the block failed.

Is this an issue with the way the context is setup?


Solution

  • MagicalRecord's context sets up its own queue to perform operations on it. Therefore, if you want to use it to look something up, you have to do it in a block ([context performBlock:] or [context performBlockAndWait:]) so that it can perform it on its own queue and then return back to the NSOperation.

    What I ended up doing was creating a couple __block properties and then pulling the needed contents from the object outside of the block.