1

我有一个对象,锻炼,它与一个对象,锻炼有一对多的关系。

模型图:http: //i.imgur.com/q1Mfq.png

当我创建一个锻炼对象时,我通过循环添加三个锻炼对象

[self addExercisesObject:exercise]

然后保存我的托管对象上下文。然后,从用于显示锻炼的控制器中,我可以成功获取锻炼及其锻炼(使用获取请求),如调试器中的输出所示:

Printing description of self->_savedWorkout:
<Workout: 0x6c5a990> (entity: Workout; id: 0x6e46e00 <x-coredata://EA417EAA-101A-4F04-8276-3C4A6CDF094D/Workout/p1> ; data: {
bodyweight = nil;
date = "2012-05-09 16:59:43 +0000";
exercises =     (
    "0x6e3c870 <x-coredata://EA417EAA-101A-4F04-8276-3C4A6CDF094D/Exercise/p3>",
    "0x6e3eaf0 <x-coredata://EA417EAA-101A-4F04-8276-3C4A6CDF094D/Exercise/p2>",
    "0x6e36820 <x-coredata://EA417EAA-101A-4F04-8276-3C4A6CDF094D/Exercise/p1>"
);
isCompleted = 0;
workoutId = 1;
workoutPlan = "0x6e6c980 <x-coredata://EA417EAA-101A-4F04-8276-3C4A6CDF094D/WorkoutPlan/p1>";
})

到现在为止还挺好。但是,如果我在模拟器中关闭我的应用程序并重新启动它并在同一视图中执行相同的获取请求,则锻炼将如下所示:

Printing description of self->_savedWorkout:
<Workout: 0x6ea8ff0> (entity: Workout; id: 0x6e8f9e0 <x-coredata://EA417EAA-101A-4F04-8276-3C4A6CDF094D/Workout/p1> ; data: {
bodyweight = nil;
date = "2012-05-09 16:59:43 +0000";
exercises =     (
);
isCompleted = 0;
workoutId = 1;
workoutPlan = "0x6c8a130 <x-coredata://EA417EAA-101A-4F04-8276-3C4A6CDF094D/WorkoutPlan/p1>";
})

似乎它获取了相同的锻炼对象,但现在锻炼是一个空集。实际上,在 fetch 请求之后,练习首先看起来像这样:

exercises = "<relationship fault: 0x8a93100 'exercises'>";

但一旦我这样做:

for (Exercise *exercise in self.savedWorkout.exercises)

self.savedWorkout.exercises 解析为一个空集。我不会在我的应用程序的任何部分编辑锻炼。

我的获取请求是通过我的 Workout 类中的此方法发出的:

- (Workout *)getLatestWorkout
{
  self.model = [[self.managedObjectContext persistentStoreCoordinator] managedObjectModel];

  NSFetchRequest *fetchRequest = [self.model fetchRequestTemplateForName:@"getLatestWorkout"];

  NSError *error = nil;
  NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
  if ([results count] == 1) {
    return [results objectAtIndex:0];
  }
  return nil;
}

我使用 Xcode 的 GUI 工具制作了获取请求模板。它获取 isCompleted == 0 的所有 Workout 对象。您可以看到它每次都获取相同的对象,因为在两个调试器输出中,锻炼的 x-coredata 路径是相同的。

更新:我检查了我的 SQLite 数据库。锻炼表中有一项锻炼,锻炼表中有三项锻炼。

有什么想法吗?

编辑:创建下面发布的对象的代码

- (void)storeUserSettings
{
  // get the file path if it exists
  NSString *path = [[NSBundle mainBundle] pathForResource:@"userSettings" ofType:@"plist"];

  // create it if it doesn't
  if (path == nil) {
    path = [NSString stringWithFormat:@"%@%@", 
            [[NSBundle mainBundle] bundlePath], @"/userSettings.plist"];
  }

  // and write the new settings to file
  [self.userSettings writeToFile:path atomically:YES];

  // load managed object context
  [self loadMOC];

  WorkoutPlan *currentPlan = [[WorkoutPlan alloc] getActiveWorkoutPlan];

  [currentPlan setManagedObjectContext:self.managedObjectContext];

  // if user has no plan or is changing plans, create new plan and first workout
  if (currentPlan == nil || 
      ([self.userSettings valueForKey:@"plan"] != currentPlan.planId)) {

    // create a workoutPlan object
    WorkoutPlan *workoutPlan = [[WorkoutPlan alloc] initWithEntity:
                                [NSEntityDescription entityForName:@"WorkoutPlan" 
                                            inManagedObjectContext:self.managedObjectContext] 
                                    insertIntoManagedObjectContext:self.managedObjectContext];

    // set attributes to values from userSettings and save object
    [workoutPlan createWorkoutPlanWithId:[self.userSettings valueForKey:@"plan"] 
                                schedule:[self.userSettings valueForKey:@"schedule"]
                             dateStarted:[self.userSettings valueForKey:@"nextDate"]];
  }
  // if user is just changing schedule, update schedule of current plan
  else if (![currentPlan.schedule isEqualToString:[self.userSettings valueForKey:@"schedule"]]) {
    [currentPlan setSchedule:[self.userSettings valueForKey:@"schedule"]];

    [currentPlan saveMOC];
  }
}

- (void)loadMOC
{
  AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
  self.managedObjectContext = delegate.managedObjectContext;
  self.model = [[self.managedObjectContext persistentStoreCoordinator] managedObjectModel];
}

- (void)createWorkoutPlanWithId:(NSNumber *)planId schedule:(NSString *)schedule 
                    dateStarted:(NSDate *)dateStarted
{ 
  [self deactivateCurrentPlan];

  // set workout plan attributes
  [self setPlanId:planId];
  [self setIsActive:[NSNumber numberWithBool:YES]];
  [self setSchedule:schedule];
  [self setDateStarted:dateStarted]; 

  // create first workout and add to workout plan

  Workout *firstWorkout = [NSEntityDescription
                          insertNewObjectForEntityForName:@"Workout"
                          inManagedObjectContext:self.managedObjectContext];

  [firstWorkout setManagedObjectContext:self.managedObjectContext];

  [firstWorkout createFirstWorkoutForPlan:self onDate:dateStarted];

  [self addWorkoutsObject:firstWorkout];

  [self saveMOC];
}

- (void)createFirstWorkoutForPlan:(WorkoutPlan *)plan onDate:(NSDate *)startDate
{
  // set workout attributes
  [self setDate:startDate];
  [self setIsCompleted:[NSNumber numberWithBool:NO]];
  [self setWorkoutId:[NSNumber numberWithInt:1]];

  NSArray *exerciseList = [self getExercisesForWorkout:self inPlan:plan];

  // iterate over exercises in spec and create them
  for (NSDictionary *exerciseSpec in exerciseList) 
  {
    // create a exercise MO
    Exercise *exercise = [NSEntityDescription
                          insertNewObjectForEntityForName:@"Exercise"
                          inManagedObjectContext:[plan managedObjectContext]];

    [exercise setManagedObjectContext:[plan managedObjectContext]];
    [exercise createExerciseForWorkout:self withSpec:exerciseSpec];

    // add exercise to workout object
    [self addExercisesObject:exercise];
  }

}

- (void)createExerciseForWorkout:(Workout *)workout withSpec:exerciseSpec
{
  // set exercise attributes
  self.exerciseId = [exerciseSpec valueForKey:@"id"];
  self.isPersonalRecord = [NSNumber numberWithBool:NO];

  NSArray *sets = [exerciseSpec valueForKey:@"sets"];
  int i = 1;
  for (NSNumber *setReps in sets)
  {
    // create a set MO
    Set *set = [NSEntityDescription
                          insertNewObjectForEntityForName:@"Set"
                          inManagedObjectContext:[workout managedObjectContext]];

    [set setManagedObjectContext:[workout managedObjectContext]];

    // set set attributes
    set.order = [NSNumber numberWithInt:i];
    set.repetitions = setReps;
    set.weight = [exerciseSpec valueForKey:@"default_weight"]; 

    // add set to exercise object
    [self addSetsObject:set];
    i++;
  }
}

4

3 回答 3

1

我有一个类似的问题。应用程序运行时父子关系有效,但重新启动后仅检索到最新的子记录。

我正在添加这样的孩子:

  • 创建子记录
  • 设置孩子的父属性,设置孩子的其他属性
  • 使用父级的 add 方法将子级添加到父级

如果我这样做,我发现它是固定的:

  • 创建子记录
  • 使用父级的 add 方法将子级添加到父级
  • 设置孩子的父属性,设置孩子的其他属性
于 2012-10-13T22:47:27.963 回答
0

核心数据很复杂。可能有几十件事要检查,任何一件都可能导致问题。

您使用了多少个 MOC?你怎么存钱?还有很多问题...

我建议在启动应用程序时为参数打开 EditScheme 中的 SQL 调试标志 (-com.apple.CoreData.SQLDebug 1)。

运行你的代码,看看实际发生了什么。

获取时关系解析为错误,除非您在获取请求中覆盖它。

如果您在父/子关系中使用多个 MOC,则从子级到父级的保存只是将数据放入父级,并没有真正保存它。如果使用 UIManagedDocument,这是一组完全不同的问题......

我希望这听起来不刺耳。准备好为 Core Data 问题提供大量信息,而不是“这不是保存,这是一些调试输出”。

基本上,CoreData 的工作方式取决于堆栈的创建方式、是否使用 UIManagedDocument、多个线程、如何创建对象、如何保存它们、获取请求的选项以及更多其他事情。

它实际上并没有那么复杂,但是有很多自定义和特殊情况取决于它的使用方式。

编辑

发布创建对象/关系的代码。

此外,尝试使用手动提取请求而不是模板进行提取。当您查看数据库中的数据时,您是否看到适当设置的关系的外键?

在启用调试的情况下再次运行它,以查看 SQL 正在做什么。这比您自己的调试输出更有价值。

于 2012-05-09T19:27:44.493 回答
0

我遇到了完全相同的问题,但我的模型非常复杂。如果实体和关系不存在,我的应用会在启动时创建它们。如果它们被创建并且我没有退出应用程序,我可以获取具有一对多关系的实体并查看相关对象的正确计数。如果我退出我的应用程序并重新启动它(它现在知道它不必创建默认数据集),那么关系将返回一个空集。我想不通。

编辑:我发现我的问题与有序集合关系有关。我必须使用 Category 来创建解决方法(在堆栈溢出时发现)将新条目插入到有序集中。所以我猜这与它有关。

于 2012-08-13T02:00:34.563 回答