我遇到了一个非常令人沮丧的问题,希望帮助您理解甚至解决。

我正在通过构建一个相当简单的应用程序来学习Core Data。

我的模型如下:

用户

属性


名称
性别
多宝


人际关系


hasCompletedItem-“用户可以完成许多列表项,列表项可以由许多用户完成”
isInAgeGroup-“一个用户在一个AgeGroup中,一个AgeGroup可以包含多个用户”


年龄阶层

属性


标题


人际关系


hasItems-“一个AgeGroup有许多与其关联的ListItem,一个ListItem只能与一个AgeGroup关联”
hasUsers-“一个AgeGroup有许多与其关联的用户,一个用户只能在1个AgeGroup中”


项目清单

属性


itemText


人际关系


completeByUser-“一个列表项可以由许多用户完成,一个用户可以完成许多列表项”。
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。它在表格中列出每个用户。它具有一个允许添加新用户的按钮。表格视图的数据由NSFetchedResultsController提供,该控制器基本上只是从User实体获取所有记录。
轻按“添加”按钮将加载AddProfileViewController,但不会在传递managedObjectContext之前加载:




AddProfileViewController *viewController = (AddProfileViewController *)[[segue destinationViewController] topViewController];
[viewController setManagedObjectContext:self.managedObjectContext];



AddProfileViewController只是一个UIViewController。它具有用于名称,D.O.B等的textFields。
它具有方法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:错误:无法解决开放式锁定失败:具有(空)的开放式锁定失败
CoreData:错误:无法解决乐观锁定失败。旧的保存请求为:{插入((
    “ 0x942dd40”
)), 更新 ((
    “ 0x8e4ed60”
)),删除()锁()}
2014-04-12 20:00:09.482 ChekList [6076:60b] CoreData:错误:无法解决乐观锁定失败。下一次尝试将是:{插入((
    “ 0x942dd40”
)), 更新 ((
    “ 0x8e4ed60”
)),删除()锁()}
sql:开始独家
注释:为entityID = 3获取最大pk
注释:更新entityID = 3的最大pk,旧= 4017,新= 4018
sql:提交
sql:开始独家
sql:更新ZAGEGROUP SET Z_OPT =吗? Z_PK =吗? AND(Z_OPT =?或Z_OPT为NULL)
详细信息:SQLite bind [0] =(int64)1
详细信息:SQLite bind [1] =(int64)6
详细信息:SQLite bind [2] = nil
sql:回滚
sql:从ZAGEGROUP WHERE Z_PK IN(6)中选择Z_PK,Z_OPT
注释:sql执行时间:0.0006s


我的理解是,基本上其他内容已修改了上下文,并且在保存期间,CoreData已注意到并停止了该上下文。我已经读过有关更改ManagedObjectContext上的MergePolicy的信息,但是我真的不想在不知道为什么要这样做的情况下执行此操作。

有趣的是,如果我注释掉试图设置关系的那一行,它就可以正常工作。 (当然不设置关系)

据我所知,我正在正确地将ManagedObjectContext传递给每个视图控制器。我还确保不存在访问同一持久性存储的多个上下文。

由于某种原因,很可能是前一个View Controller中的FetchedResultsController修改了上下文吗?

有谁能够提供一些信息,也许是可能的解决方案?我宁愿不必更改合并策略。我不明白为什么我必须考虑一个相当简单的例子。我整天大部分时间都在梳头。

我忍不住认为这很可能是我所缺少的简单事物。

谢谢,
布雷特

最佳答案

我能够找出解决方案。

原来,我的整个问题是由我的预加载数据库引起的。
我有一个预加载的数据库,可以在AppDelegate中进行复制。原来这是有问题的。

通过注释掉复制数据库的行,然后在AppDelegate中手动添加了预加载的数据,我发现了这一点。我能够添加数据并设置关系。

从那里我创建了一个新的预加载数据库,它工作正常。

我还找到了一个很棒的Apple开发人员示例-

https://developer.apple.com/library/ios/samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html

这也向我展示了添加新实体的另一种方法。在这种情况下,他们在prepareForSegue方法中创建实际实体,并仅传递实体,而不传递整个上下文。

我更改了我的应用程序以也使用该方法(在我发现数据库是问题之前),但仍然崩溃。我决定保留这种方式,尽管它看起来更优雅。

关于ios - iOS核心数据“乐观锁定失败”,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23028981/

10-12 23:26