CoreData 中的自引用数据对象

Posted

技术标签:

【中文标题】CoreData 中的自引用数据对象【英文标题】:Self-Referencing Data Object in CoreData 【发布时间】:2013-12-07 21:51:49 【问题描述】:

我正在从一生的 SQL 转换到 CoreData,这并非没有一些小问题。此刻让我兴奋的事情是创建一个自引用对象。请允许我解释一下。

使用 SQL 术语,我有一个包含流程中的步骤的表。为了简单起见,我会说该表包含两条信息 - 步骤的名称和紧随其后的步骤(可能是也可能不是表中的下一条记录)。流程中的所有步骤都存储在一个表中。每一步总会有一个步骤在它之后。但并不是所有的步骤都有前面的步骤。

换句话说,它应该看起来像这样:

(来源:justinwhitney.com)

在 SQL 世界中,我会创建一个包含标识字段、名称和引用其自身标识字段的外键(我猜那将是国内键?)的表,因此:

(来源:justinwhitney.com)

但是,对于关系,没有身份字段之类的东西。也没有我可以创建的选择查询来提取我需要的信息。

那么创建一个做同样事情的实体的最佳方法是什么?我尝试创建一种与自身相反的关系,结果证明这是一场难以调试的灾难。还有什么其他选择?

谢谢!

【问题讨论】:

【参考方案1】:

创建从实体到自身的关系“nextStep”。然后你可以 做类似的事情

// Create first thing:
Thing *thingA = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:context];
thingA.name = @"...";

// Create second thing:
Thing *thingB = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:context];
thingB.name = @"...";

// Establish the relationship between these two objects:
thingA.nextStep = thingB;

“nextStep”也应该具有“反向关系”。由于两个或多个对象 可以有相同的后继(如你的情况,“C”和“D”都指向“E”), 反向关系将是“对多”关系,可以称为 "previousSteps" 或类似的。

在 Core Date 模型编辑器中,如下所示:

【讨论】:

【参考方案2】:

啊!你提供了重要的线索,马丁。

我尝试了代码示例,但效果不佳。它最终创建了所有内容的副本,因为 thingA 和 thingB 都插入了表中。然而,这张图实际上给了我我认为可能是关键的东西。以前我曾尝试将 nextStep 指定为它自己的逆关系,这简直是疯了。但只是添加 previousSteps 并将其设置为 Many 而 nextStep 设置为 One 似乎导致了解决方案。

这是我最终为关系创建的内容:

(来源:justinwhitney.com)

这是我用来填充 Steps 实体的 plist(打算在首次使用或重置数据库时运行):

(来源:justinwhitney.com)

现在这里是实体填充例程。这就是昨天让我绊倒的原因:

- (IBAction)populateSteps:(UIButton *)sender 
    
    NSString *responseString;
    
    BOOL isStepsPopulated = [[UserPrefs loadUserData:@"didPopulateSteps"] boolValue];
    
    if (!isStepsPopulated) 
        
        NSDictionary *stepDict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"DefaultSteps" ofType:@"plist"]];
        NSArray *stepArray = [stepDict objectForKey:@"Steps"];
        
        //1
        __block NSMutableDictionary *stepObjectDict = [[NSMutableDictionary alloc] initWithCapacity:[stepArray count]];
        
        //2
        [stepArray enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) 
            
            //3
            Steps *thisStep = [NSEntityDescription insertNewObjectForEntityForName:@"Steps" inManagedObjectContext:self.managedContext];
            thisStep.stepName = [dict objectForKey:@"StepName"];
            
            //4
            [stepObjectDict setObject:thisStep forKey:thisStep.stepName];
            
        ];
        
        //5
        [stepArray enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) 
            Steps *thisStep = [stepObjectDict objectForKey:[dict objectForKey:@"StepName"]];
            Steps *nextStep = [stepObjectDict objectForKey:[dict objectForKey:@"NextStep"]];
            thisStep.nextStep = nextStep;
        ];
        
        NSError *error = nil;
        [self.managedContext save:&error];
        if (error) 
            responseString = [NSString stringWithFormat:@"Error populating Steps: %@", error.description];
         else 
            responseString = @"Steps have been populated.";
            [UserPrefs saveUserData:[NSNumber numberWithBool:TRUE] forKey:@"didPopulateSteps"];
        

     else 
        responseString = @"Steps already populated.";
    
    
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Populate Steps" message:responseString delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];

这是关键。在 (1) 创建一个 NSMutableDictionary 来保存迭代的结果,使用 stepName 作为 Key,以便以后可以引用它。

在 (2) 枚举 plist 内容时,(3) 像您一样创建第一个 NSEntityDescription。但不是创建第二个,而是 (4) 将其添加到字典中。

一旦完成初始枚举,(5) 再返回第二次。但是这一次,通过引用原始对象本身来创建关系。有意义吗?

之后,正常保存上下文。在这种情况下,结果是 5 条记录,每条记录都引用同一实体中的另一条记录,没有冲突或重复。

(来源:justinwhitney.com)

现在重要的问题是:如何将其标记为已回答?谁得到复选标记?

【讨论】:

这完全有道理。实际上,我的代码示例主要是为了展示如何在两个对象之间建立关系(因为我不知道 如何 填充您的数据)。 - 接受哪个答案完全取决于您。

以上是关于CoreData 中的自引用数据对象的主要内容,如果未能解决你的问题,请参考以下文章

Cocoa:CoreData - ManagedObjectContext 中的多个实体

CoreData 的自定义 UID 数据类型

谷歌地图 SDK - 核心数据

iOS中的自定义排序,希望在Coredata的顶部显示默认(所有组)标题

NHibernate 中的自引用实体给对象引用一个未保存的瞬态实例异常

CoreData + mogenerator - 如何防止中间数据模型中的`setValue(forKey :)`引用最终数据模型中实体的'人类'类?