R – Model instantiation question when using Core Data

cocoacocoa-touchcore-dataiphoneobjective c

I'm slightly confused in one aspect of Core Data. That is, when do I use the rudimentary alloc/init routine vs created an object with core data and saving it into the current managed object context.

I know that's a rather vague question, so let me give you an example.

I have an application I'm currently working on that iterates through all of a user's contact book on the iPhone. From there, I wrote a model class called 'Person'. I used to do something like this in a loop of people:

Person *person = [[Person alloc] initWithWrapper:mywrapper];

mywrapper would contain an NSDictionary with the attributes for person. Later I'd be able to populate the address book in my app with the person objects.

Now I've started rebuilding parts of the app with Core Data. Do I continue using the strategy above to populate my address book? Or do I do something like this instead:

    Person *person = (Person *)[NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:managedObjectContext];

    [person setName:name];
    [person setDob:dob];

    // Commit the change.
    NSError *error;
    if (![managedObjectContext save:&error]) {
        // Handle the error.
    }

The problem is, this code gets executed everytime the app gets started. Should I not be using core data as it will populate the storage mechanism with redundant instances of person everytime the app loads? Should I modify my NSManagedObject (Person class) and add my initWithWrapper: method and continue as I normally would there?

Slightly confused, would love clarification.

Best Answer

You should never be initializing Core Data objects outside of a managed object context - there's simply no point. Having some

Person *person = [[[Person alloc] init] autorelease];

does you no good since you can't save the object, manipulate it, or really do anything useful that Core Data provides without the context (and thus model and store coordinator) backing it up.

You should instead only use the alloc-init combo when you are inserting an object into Core Data for the first time; this is what the initWithEntity:insertIntoManagedObjectContext: method is for. And you're right, every time you call that method you are inserting a new object into the Core Data context and therefore store, and you may wind up with duplicate objects if you're not careful.

What I would instead recommend for you, if you're running code on every startup, is to come up with a Core Data query that returns some set of existing Person objects, and only add objects (using the initialization method) that don't already exist in the store. If the object already exists, modify it instead of creating a new one.

The trick is getting something like this to perform properly. You shouldn't do a Core Data fetch for every contact in the iPhone address book; many small fetches like this are very expensive. You could in theory get two NSSets - one of Person objects, and one of contacts - then compare them by some unique key (like a hash of the first and last names of the contact). I leave the optimization to you.

The key point is this: don't use alloc and init on a Core Data object unless you mean to insert that object for the first time into a context. Instead look at your existing objects and modify them if necessary.

Related Topic