I\'m struggling to figure out a decent solution to a problem that arises when using nested Managed Object Contexts in Core Data. Take a model that has two enites, Person and Nam
I ended up going with the convenience method solution. All the NSManagedObject subclasses in my app have a +insertInManagedObjectContext:
method. Creating instances of those objects (in my own code) is always done using that method. Inside that method, I do this:
+ (instancetype)insertInManagedObjectContext:(NSManagedObjectContext *)moc
{
MyManagedObject *result = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntityName" inManagedObjectContext:moc]
[result awakeFromCreation];
return result;
}
- (void)awakeFromCreation
{
// Do here what used to be done in -awakeFromInsert.
// Set up default relationships, etc.
}
As for the NSArrayController issue, solving that isn't bad at all. I simply created a subclass of NSArrayController, overrode -newObject
, and used that subclass for all the relevant NSArrayControllers in my app:
@implementation ORSManagedObjectsArrayController
- (id)newObject
{
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:[self entityName]
inManagedObjectContext:moc];
if (!entity) return nil;
Class class = NSClassFromString([entity managedObjectClassName]);
return [class insertInManagedObjectContext:moc];
}
@end
The first thought that comes to mind is that while Name
's person
relationship is non-optional, you didn't say that Person
's name
relationship is also non-optional. Is it OK to create a Person
with no Name
, dispose with your code, and then create the Name
later on when you actually need it?
If not, one simple way is just to check whether you're on the root context before creating a Name
:
- (void)awakeFromInsert
{
[super awakeFromInsert];
NSManagedObjectContext *context = [self managedObjectContext];
if ([context parentContext] != nil) {
self.name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context];
}
}
But that only works if you always create new instances on a child context and you never nest contexts more than one level deep.
What I'd probably do instead is create a method like the insertNewPersonInManagedObjectContext:
that you describe. Then supplement it with something like the following to handle any cases where instances get created for you (i.e. array controllers):
- (void)willSave
{
if ([self name] == nil) {
NSManagedObjectContext *context = [self managedObjectContext];
Name *name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context];
[self setName:name];
}
}
...and of course don't bother with a custom awakeFromInsert
...