CoreData中的自引用数据对象

时间:2013-12-07 21:51:50

标签: ios sql objective-c core-data

我正在从一生的SQL到CoreData进行心理转换,并且不是没有几点打嗝。目前正在踢我的是创建一个自引用对象。请允许我解释一下。

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

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

desired outcome http://justinwhitney.com/images/desiredOutcome.png

在SQL世界中,我会创建一个带有标识字段的单个表,名称和引用其自己的标识字段的外键(我猜这将是一个国内密钥?),因此:

SQL version http://justinwhitney.com/images/usingSQL.png

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

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

谢谢!

2 个答案:

答案 0 :(得分:3)

从实体到自身创建关系“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”或类似的。

在核心日期模型编辑器中,这看起来像:

答案 1 :(得分:3)

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

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

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

Steps entity Attributes and Relationships http://justinwhitney.com/images/StepsAttributesRelationships.png

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

DefaultSteps.plist http://justinwhitney.com/images/DefaultSteps_plist.png

现在这是实体人口常规。这就是昨天绊倒我的事情:

- (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条记录,每条记录引用同一实体中的另一条记录,没有冲突或重复。

Final Result http://justinwhitney.com/images/FinalResult.png

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