如何在使用嵌套上下文时自动设置Core Data关系

时间:2013-03-12 05:07:20

标签: ios objective-c macos cocoa core-data

我正在努力找到一个合适的解决方案来解决在核心数据中使用嵌套的托管对象上下文时出现的问题。采用具有两个enites的模型,Person和Name,其中每个Person与Name具有一对一的关系,而Name的person关系不是可选的。以前,在Person的-awakeFromInsert方法中,我会自动为新Person创建一个Name实体:

- (void)awakeFromInsert
{
    [super awakeFromInsert];

    NSManagedObjectContext *context = [self managedObjectContext];
    self.name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context];
}

这在单个非嵌套的托管对象上下文中工作得很好。但是,如果上下文具有父上下文,则在保存子上下文时,将在父上下文中创建新的Person对象,并在原始对象之前再次在此新对象上调用-awakeFromInsert将复制人员的属性和关系。因此,创建另一个Name对象,然后在复制现有名称关系时“断开连接”。保存失败,因为浮动Name的now-nil person关系验证失败。这个问题在here和其他地方都有描述。

到目前为止,我一直无法为这个问题找到一个好的解决方案。在getter方法中懒惰地创建关系实际上会导致同样的问题,因为当在父上下文中创建新Person时,内部Core Data机制会调用getter。

我唯一能想到的就是放弃自动生成关系,并且总是在创建Person的控制器类中显式创建关系,或者只在一个方便的方法(例如+[Person insertNewPersonInManagedObjectContext:])中创建关系由我的代码调用,始终用于显式创建新Person对象的方法。也许这是最好的解决方案,但我宁愿不必如此严格,只允许使用单个方法来创建托管对象,当我无法控制的其他创建方法和其使用我不能容易检查/排除,存在。首先,它将意味着多个NSArrayController子类来自定义它们创建托管对象的方式。

是否有其他遇到此问题的人提出了一个优雅的解决方案,允许一个NSManagedObject在创建/插入时自动创建关系对象?

2 个答案:

答案 0 :(得分:1)

首先想到的是,虽然Name的{​​{1}}关系不是可选的,但您没有说明person的{​​{1}}关系也是非选择性的。是否可以创建一个没有Person的{​​{1}},处理您的代码,然后在您真正需要时创建name

如果没有,一种简单的方法就是在创建Person之前检查您是否在根上下文中:

Name

但只有当始终在子上下文中创建新实例并且永远不会嵌套多于一层的上下文时,这才有效。

我可能会做的是创建一个类似你描述的Name的方法。然后用以下内容补充它以处理为您创建实例的任何情况(即阵列控制器):

Name

...当然不要打扰自定义- (void)awakeFromInsert { [super awakeFromInsert]; NSManagedObjectContext *context = [self managedObjectContext]; if ([context parentContext] != nil) { self.name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context]; } } ...

答案 1 :(得分:1)

我最终选择了便捷方法解决方案。我的应用程序中的所有NSManagedObject子类都有+insertInManagedObjectContext:方法。使用该方法始终创建这些对象的实例(在我自己的代码中)。在这个方法中,我这样做:

+ (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.
}

对于NSArrayController问题,解决这个问题一点也不差。我只是创建了一个NSArrayController的子类,覆盖-newObject,并在我的应用程序中使用该子类用于所有相关的NSArrayControllers:

@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