Question

I have created an object in managed object context, which I haven't saved yet. Further I created a child context and want to modify this object in this context. To be able to modify this object I have to retrieve this temporary object in the child context.

I use MagicalRecord's method MR_inContext:, which use the method existingObjectWithID:error:. But this doesn't return the object for the given temporary object ID. The method objectWithID: does seems to work. I don't understand why existingObjectWithID:error: doesn't work but objectWithID: does in this case.

- (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], @"");
}
Was it helpful?

Solution

The reason existingObjectWithID:error: returns nil here is simply that the object doesn't exist for that context, because it's a temporary object from a different context.

If you create an object in one managed object context but don't save it, it only exists in that context. No other managed object context has any way of knowing about it. It's not in memory for them, and since it's not in the persistent store, there's no way for them to find it. When you call existingObjectWithID:error:, the context checks what it has in memory and, if necessary, the persistent store. For an unsaved object from a different context, neither check will find anything.

Once you save changes, the object is written to the persistent store file, and then other contexts can find it. At that point, existingObjectWithID:error: would return non-nil for the ID.

Calling objectWithID: will return an object, but not one that you can use. That method assumes that you've provided a valid object ID, so it doesn't check the persistent store. If the object exists, its data will be faulted in normally. If it doesn't, you'll get an exception. This method is useful when you know the object exists and want faster response. In your case the object doesn't exist, so calling this method gives you a time bomb that will blow up your app.

OTHER TIPS

objectWithID: always returns an object, but it may not be valid (and could throw an exception when you try to use it). existingObjectWithID:error: checks for existence before it returns and returns nil if the object couldn't be found. So, using existingObjectWithID:error: is the safe option.

You should save the new object before trying to access it in another context, or just don't use another context (it isn't clear why you want to do what you're trying to do...).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top