views:

31

answers:

1

Original question has been answered. Update addresses related question raised in comments.

Original post:

I am using the MOC save method used in Apple's CoreDataBooks. However, I seem to have use for two layers of MOC merging (three MOCs where the 3rd merges with the 2nd and then the 2nd merges with 1st).

First, I have a tableview (ClassList) listing school classes. Selecting a class pushes a 2-row tableview (AddClass). At AddClass, the first row allows the user to edit the class title. Selecting the second row pushes a tableview (ClassRoster) that displays the student roster for that class. Lastly, selecting a student pushes on another 2-row tableview (AddStudent) where the user can edit the student name and username.

I can add and save classes successfully by using the dual MOC merge method (managedObjectContext and addingManagedObjectContext as employed by CoreDataBooks). I will call the "base MOC" in my first view "MOC1" and call the "scratchpad" MOC "MOC2".

MOC2 temporarily stores changes made to a class object. These changes can then either be saved or canceled, sending a -didFinishWithSave:(BOOL) to the delegate. If I save, the changes made in MOC2 are merged with MOC1. That merge is working perfectly.

Handling changes made to student objects is where I'm going wrong. I thought I could employ MOC3 as a scratchpad for changes to student objects which would merge with MOC2 (when I saved a student object). MOC2 could in turn be saved with MOC1 when I saved the class object.

But I have run into errors with saving MOC3 and adding student objects to class objects because they are in different contexts. I can post code, but first I wanted to ask the bigger question: Am I going about this all the wrong way?

UPDATE:

Mr. Zarra recommended using initWithEntity:insertIntoManagedObjectContext: and setting the MOC to nil, thereby creating a temporary object which could later have its MOC set and saved.

Following his advice, I am attempting to incorporate the following code:

    NSManagedObjectModel *managedObjectModel - [[managedObjectContext persistentStoreCoordinator] managedObjectModel];
    NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"MyClass"];
    MyClass *newClass = [[MyClass alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

I have run into an error, but I'm not sure it is related to this code yet. I will debug and post what I find.

+1  A: 

Yes. You do not need to use more than one NSManagedObjectContext. That example is a very poor one. In your case you should be using a single context and that will remove all of your issues.

If you want a temporary entity, create it with a nil NSManagedObjectContext. When you want to save it you call -setManagedObjectContext: and then save that NSManagedObjectContext.

The only time you realistically want to use more than one NSManagedObjectContext is when you are in a multi-threaded situation.

Marcus S. Zarra
Thanks again Mr. Zarra. What did you mean by "that example is a very poor one"? Did you mean I wrote my question poorly? I would welcome some constructive criticism if that is the case.
Spindler
No, that example provided by Apple is a very poor one. It is a terrible example that unfortunately gets copied constantly and then defended because "Apple wrote it".
Marcus S. Zarra
Ah...well I'm happy to use whatever works :P I had tried taking a custom approach with little success before I used the Apple example and that at least got my cancel and save functions syncing properly. But I agree...the Apple code seems unnecessarily dense and non-intuitive. Do you know of any public examples that are better-written?
Spindler
Might be some items either on [CIMGF.com](http://www.cimgf.com) or [Mac Developer Network](http://www.mac-developer-network.com/). You can use the Apple example, just remove the extra MOCs and push around the single MOC. When you create a new entity in your add view just use its `-initWithEntity:insertIntoManagedObjectContext:` but pass nil as the `NSManagedObjectContext`. Pretty straight forward.
Marcus S. Zarra
Sounds good. Thanks :)
Spindler
I am using this line to initialize my temporary entity: `addViewController.newClass = (myClass *) [NSEntityDescription insertNewObjectForEntityForName: @"myClass" inManagedObjectContext:nil];` Is that what I should be using? I didn't see a a method like the one you wrote in the comment above. What am I doing wrong if that line kicks back this error? "'NSInternalInconsistencyException', reason: '+entityForName: could not locate an NSManagedObjectModel for entity name 'myClass'"
Spindler
1) Edit your question and add this, the formatting is better. 2) Don't lowercase your class names; that breaks convention. 3) Don't cast from `id`, it is unnecessary. 4) The `-initWithEntity:insertIntoManagedObjectContext:` is on the `NSManagedObject` itself, not on the `NSEntityDescription`. You can search the documentation to find it.
Marcus S. Zarra
I am making some progress, so I will edit my post when I have something fresh to say or ask. I don't understand what "casting from id" means. Does that have to do with `(MyClass *)`? I don't really understand that syntax. I just see it used all of the time, so I mimic it.
Spindler
If you don't understand something, remove it. Then you will know if it was needed or not. Likewise, If you are casting in Objective-C you are doing it wrong. Unfortunately, lots of people are doing it wrong.
Marcus S. Zarra