views:

1186

answers:

1

I'm creating a addingManagedObjectContext as a scratch pad area for new entities and then merge the new entity into my main ManagedObjectContext on 'Save', similar to how it's shown in CoreDataBooks Example.

After you merge your new entity, how do you get a quick reference to it for use in displaying a detail view?

Do you have to make the fetch result controller go out and perform a fetch again (expensive as noted in the CoreDataBooks code)? I'm presuming that the initial id of the object when in the 'addingManagedObjectContext' will not remain the same after it has been merged.

In the Recipes project, there isn't this concern because you're creating and working with the new entity in one ManagedObjectContext. Therefore, you have a reference to the newly created item to show a detail view of it.

From CoreDataBooks:

/**
 Add controller's delegate method; informs the delegate that the add operation has completed, and indicates 
 whether the user saved the new book.
 */
- (void)addViewController:(AddViewController *)controller didFinishWithSave:(BOOL)save {

    if (save) {
     /*
      The new book is associated with the add controller's managed object context.
      This is good because it means that any edits that are made don't affect the 
      application's main managed object context -- it's a way of keeping disjoint edits 
      in a separate scratchpad -- but it does make it more difficult to get the new book 
      registered with the fetched results controller.

      First, you have to save the new book.  This means it will be added to the persistent 
      store.  Then you can retrieve a corresponding managed object into the application 
      delegate's context.  Normally you might do this using a fetch or using objectWithID: -- for example

      NSManagedObjectID *newBookID = [controller.book objectID];
      NSManagedObject *newBook = [applicationContext objectWithID:newBookID];

      These techniques, though, won't update the fetch results controller, which 
      only observes change notifications in its context.

      You don't want to tell the fetch result controller to perform its fetch again 
      because this is an expensive operation.

      You can, though, update the main context using mergeChangesFromContextDidSaveNotification: which 
      will emit change notifications that the fetch results controller will observe.
      To do this:
      1 Register as an observer of the add controller's change notifications
      2 Perform the save
      3 In the notification method (addControllerContextDidSave:), merge the changes
      4 Unregister as an observer
      */
     NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
     [dnc addObserver:self selector:@selector(addControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];

     NSError *error;
     if (![addingManagedObjectContext save:&error]) {
      // Update to handle the error appropriately.
      NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
      exit(-1);  // Fail
     }
     [dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];
    }
    // Release the adding managed object context.
    self.addingManagedObjectContext = nil;

    // Dismiss the modal view to return to the main list
    [self dismissModalViewControllerAnimated:YES];
}


/**
 Notification from the add controller's context's save operation. This is used to update the 
 fetched results controller's managed object context with the new book instead of performing 
 a fetch (which would be a much more computationally expensive operation).
 */
- (void)addControllerContextDidSave:(NSNotification*)saveNotification {

    NSLog(@"addControllerContextDidSave");
    NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
    // Merging changes causes the fetched results controller to update its results
    [context mergeChangesFromContextDidSaveNotification:saveNotification]; 
}
+1  A: 

Looking at those comments, it looks like they're talking about fetched results controllers. So it would be rather expensive to get the FRC to perform a new fetch after you've just changed one object, therefore you can merge your adding context with it to notify it of any changes.

Once you have performed the save and merge, any managed objects that you have a reference to in the adding context won't have temporary object ID's any more as they exist in the persistent store. Therefore you can remember the ID's and use [applicationContext objectWithID:newBookID] in the main application context just fine to get a handle on the object you're looking for. That will return the object (with all it's changes) in the application's context.

After the merge, it's probable that the object exists in memory and no trip to the store is required. However, even if it is, as you're only dealing with a single object to display in a detail view it's not a problem at all. Going to the store instead of in-context-memory is slower but obviously has to happen a whole bunch of times during your app and it's unlikely to cause issues unless you're dealing with a LOT of data!

Hope this helps!

Michael Waterfall
Alexi Groove
Yep, you'll be able to use that objectID in any other contexts you have, and assuming you have merged the changes from the adding context you'll have the most up to date object.
Michael Waterfall