11

我正在努力找出一个体面的解决方案来解决在核心数据中使用嵌套托管对象上下文时出现的问题。以一个有两个实体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 对象,并在复制原始 Person 的属性和关系之前-awakeFromInsert在此新对象上再次调用。因此,创建了另一个 Name 对象,然后在复制现有名称关系时“断开连接”。保存失败,因为对浮动名称的 now-nil关系的验证失败。这个问题在这里和其他地方都有描述。person

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

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

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

4

2 回答 2

1

想到的第一个想法是虽然Name' 的person关系是非可选的,但你没有说Person' 的name关系也是非可选的。是否可以创建一个Personwith no Name,使用您的代码进行处理,然后Name在您实际需要时创建后者?

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

- (void)awakeFromInsert
{
    [super awakeFromInsert];

    NSManagedObjectContext *context = [self managedObjectContext];
    if ([context parentContext] != nil) {
        self.name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context];
    }
}

但这只有在您总是在子上下文上创建新实例并且您永远不会嵌套超过一层的上下文时才有效。

我可能会做的是创建一个像insertNewPersonInManagedObjectContext:你描述的方法。然后用类似下面的东西来补充它来处理为你创建实例的任何情况(即数组控制器):

- (void)willSave
{
    if ([self name] == nil) {
        NSManagedObjectContext *context = [self managedObjectContext];
        Name *name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context];
        [self setName:name];
    }
}

...当然不要打扰自定义awakeFromInsert...

于 2013-03-12T17:58:34.160 回答
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 的子类 overrode ,-newObject并将该子类用于我的应用程序中的所有相关 NSArrayController:

@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
于 2013-03-15T17:21:50.580 回答