【发布时间】:2014-04-12 10:12:05
【问题描述】:
我有一个非常令人沮丧的问题,我想帮助理解甚至解决。
我正在通过构建一个相当简单的应用程序来学习 Core Data。
我的模型如下:
用户
属性
- 姓名
- 性别
- dob
关系
hasCompletedItem - “一个用户可以完成很多列表项,一个列表项可以被很多用户完成”
isInAgeGroup - "一个用户在一个 AgeGroup,一个 AgeGroup 可以包含多个用户"
年龄组
属性
- 标题
关系
hasItems - “一个 AgeGroup 有许多与之关联的 ListItem,一个 ListItem 只能与一个 AgeGroup 关联”
hasUsers - “一个 AgeGroup 有很多与之关联的用户,一个用户只能在 1 个 AgeGroup 中”
列表项
属性
- 项目文本
关系
completedByUser - “一个列表项可以由多个用户完成,一个用户可以完成多个列表项”。
forAgeGroup - "一个列表项被分配给一个 AgeGroup,一个 AgeGroup 可以有多个 listItems"
我的程序设置如下:
- AppDelegate 处理创建 CoreData 堆栈。
- didFinishLaunchingWithOptions 访问 MenuViewController 并通过 managedObjectContext 供其使用:
id navigationController = [[self window] rootViewController];
id controller = [navigationController topViewController];
[controller setManagedObjectContext:[self managedObjectContext]];
- MenuViewController 显示。它是一个带有几个按钮的 UIViewController,每个按钮都连接到其他视图控制器。
- 当在 MenuViewController 上按下“开始”按钮时,现有的 managedObjectContext 会在 prepareForSegue 方法中传递:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"selectprofile"]) {
[[segue destinationViewController] setManagedObjectContext:[self managedObjectContext]];
}
}
- 然后显示 SelectProfileTableViewController。它在表格中列出了每个用户。它有一个允许添加新用户的按钮。 tableview 的数据由 NSFetchedResultsController 提供,它基本上只是从 User 实体中获取所有记录。
- 点击“添加”按钮将加载 AddProfileViewController,但不会在传递 managedObjectContext 之前:
AddProfileViewController *viewController = (AddProfileViewController *)[[segue destinationViewController] topViewController];
[viewController setManagedObjectContext:self.managedObjectContext];
- AddProfileViewController 只是一个 UIViewController。它具有名称、DOB 等文本字段。
- 它有一个 addProfileDone 方法,当用户点击“完成”按钮时会调用该方法。在这个方法中,创建了一个新的 User managedObject 并设置了它的属性:
User *newMO = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:_managedObjectContext];
newMO.name = self.nameTextField.text;
newMO.dob = _dob;
// etc
- 接下来,它还会尝试设置这个新的 User 实体与其对应的 AgeGroup 实体之间的关系。
NSFetchRequest *ageGroupRecordRequest = [NSFetchRequest fetchRequestWithEntityName:@"AgeGroup"];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"title"
ascending:YES];
[ageGroupRecordRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
// Make a predicate to find the correct AgeGroup based on what was calculated
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(title == %@)", ageGroup];
[ageGroupRecordRequest setPredicate:predicate];
// Run the fetch request, should only ever get 1 result back.
NSError *fetchError;
// Result will be an array with a single AgeGroup entity
NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:ageGroupRecordRequest error:&fetchError];
// Set up the relationship using the fetched entity
// Crashes when saving if below line is uncommented
newMO.isInAgeGroup = fetchedObjects[0];
-
如代码中所述,当取消注释该行并且 managedObjectContext 去保存时,我收到以下错误:
CoreData:错误:无法解决乐观锁定失败:乐观锁定失败(null) CoreData:错误:未能解决乐观锁定失败。旧的保存请求是:{ inserts (( “0x942dd40” )), 更新 (( “0x8e4ed60” )), 删除 () 锁 () } 2014-04-12 20:00:09.482 ChekList[6076:60b] CoreData:错误:无法解决乐观锁定失败。下一次尝试将是:{插入(( “0x942dd40” )), 更新 (( “0x8e4ed60” )), 删除 () 锁 () } sql:开始独家 注释:获取 entityID = 3 的最大 pk 注释:使用 old = 4017 和 new = 4018 更新 entityID = 3 的最大 pk sql: 提交 sql:开始独家 sql: 更新 ZAGEGROUP SET Z_OPT = ?在哪里 Z_PK = ? AND (Z_OPT = ? OR Z_OPT 为 NULL) 详细信息:SQLite 绑定 [0] = (int64)1 详细信息:SQLite 绑定 [1] = (int64)6 详细信息:SQLite 绑定 [2] = nil sql:回滚 sql: 从 ZAGEGROUP 中选择 Z_PK,Z_OPT,其中 Z_PK 在 (6) 按 Z_PK 排序 注解:sql执行时间:0.0006s
我的理解是,基本上其他东西已经修改了上下文,并且在保存期间 CoreData 注意到了这一点并停止了。我已经阅读了有关更改 ManagedObjectContext 上的 MergePolicy 的信息,但我真的不想在不知道为什么必须这样做的情况下这样做。
有趣的是,如果我注释掉试图设置关系的行,它可以正常工作。 (当然除了没有设置关系)
据我所知,我将 managedObjectContext 正确地传递给每个视图控制器。我还确保没有多个上下文访问同一个持久存储。
是否可能是之前的 View Controller 中的 FetchedResultsController 由于某种原因正在修改上下文?
是否有人能够提供一些信息以及可能的解决方案?我宁愿不必更改合并策略。我不明白为什么我必须考虑它是一个相当简单的例子。这一天我大部分时间都在拔头发。
我不禁认为这很可能是我缺少的一些简单的东西。
谢谢, 布雷特。
【问题讨论】:
标签: ios objective-c core-data