How to automatically setup Core Data relationship when using nested contexts

前端 未结 2 1009
一向
一向 2021-02-08 08:32

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

相关标签:
2条回答
  • 2021-02-08 08:47

    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
    
    0 讨论(0)
  • 2021-02-08 08:56

    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...

    0 讨论(0)
提交回复
热议问题