Thursday, July 2, 2009

Core Data - Inserting a New Managed Object

For some reason, it really bugs me that the way that you insert a new managed object into a managed object context is by using a class method on NSEntityDescription. I realize that there is no "one right" abstraction, but every time I've been away from Core Data for a while, it takes me a while to remember how to create and insert a new object because this:

    NSManagedObject *newManagedObject = [NSEntityDescription 
insertNewObjectForEntityForName:[entity name]

is completely non-intuitive for me. Although the entity description is used in the process of inserting a new managed object, there's no way you can claim it's the primary object for that action.

I may be wrong, but I think it was this way in EOF, too. Anyway, the nice thing about having a dynamic language like Objective-C that supports categories is that you don't have to live with things that don't fit your particular way of thinking.

To me, the logical place for a method that inserts a new managed object into a managed object context, would be an instance method on the context, though I could also see an argument for it being a factory method on the managed object as well.

When I'm writing my own Core Data apps, I use this category:

#import <Cocoa/Cocoa.h>
@interface NSManagedObjectContext(insert)
-(NSManagedObject *) insertNewEntityWithName:(NSString *)name;

#import "NSManagedObjectContext-insert.h"
@implementation NSManagedObjectContext(insert)
-(NSManagedObject *) insertNewEntityWithName:(NSString *)name
return [NSEntityDescription insertNewObjectForEntityForName:name inManagedObjectContext:self];


This very short little category allows me to insert new objects into a context by simply doing this:

    [context insertNewEntityWityName:[entity name]];

Which personally, I find a lot easier to remember than the first one.


alanquatermain said...

If you're using your own model classes I believe it's possible to use myModelObject = [[MyModelClass alloc] init] followed by a [context insert: myModelObject]. This will insert the object the next time -save: is sent to the context.

Andrew said...

You can also use the method initWithEntity:insertIntoManagedObjectContext: when you alloc the new object.

chanson said...

Categories are useful, aren't they? :)

The one thing I'd do differently is in the naming of the method. You've named it -insertNewEntityForName: which sounds like it's inserting a new entity description, whereas it's really inserting a new instance of a named entity.

I'd probably name it -insertNewObjectForEntityForName: to mirror the method already on NSEntityDescription.

chanson said...

@alanquartermain: -init isn't the designated initializer for NSManagedObject, so (1) nothing that creates an NSManagedObject (like an NSObjectController or NSManagedObjectContext) will invoke it and (2) your own implementation shouldn't invoke it, but rather -initWithEntity:insertIntoManagedObjectContext:.

It can be useful though to implement an -init method on your own NSManagedObject subclasses, especially those that are "strongly-bound" to an entity. By which I mean they are only used by that entity, not by any subentities or other entities. (Remember that your NSManagedObject class hierarchy doesn't have to mirror your entity hierarchy at all.)

For example, this is useful in implementing scripting support in desktop applications. Cocoa's AppleScript support expects to use +alloc/-init to create objects; if you're exposing instances of an entity via AppleScript, you might override -init to look up the entity for and insert objects in some "default context for scriptability" (the current document's context, or the app context, or a completely isolated context, depending on the nature of your app).

Edwin said...

scrub m65 kamagra attorney lawyer body scrub field jacket lovegra marijuana attorney injury lawyer