我有一个基于 CoreDataBooks 示例的应用程序,它使用 addingManagedObjectContext
将 Ingredient
添加到 Cocktail
以撤消整个添加。 CocktailsDetailViewController
依次调用 BrandPickerViewController
来(可选)为给定成分设置品牌名称。 Cocktail
、Ingredient
和Brand
都是NSManagedObjects
。 Cocktail
需要至少设置一种Ingredient
(baseLiquor
),所以我在创建Cocktail
时创建它.
如果我在 CocktailsAddViewController : CocktailsDetailViewController
中添加 Cocktail
(在保存时合并到 Cocktail 托管对象上下文中)而不设置 baseLiquor.brand
,然后它可以从 CocktailsDetailViewController
的选择器(也存储在 Cocktails 托管上下文中)设置 Brand
。
但是,如果我尝试在 CocktailsAddViewController
中设置 baseLiquor.brand
,我会得到:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Illegal attempt to establish a relationship 'brand' between objects in different contexts'
来自 this question我知道问题是 Brand
存储在应用程序的 managedObjectContext
中,新添加的 Ingredient
和 Cocktail
是存储在 addingManagedObjectContext
中,传递 ObjectID
可以避免崩溃。
我不明白的是如何通用地实现选择器,以便所有成分(baseLiquor
、mixer
、garnish
、等) 可以在添加期间设置,也可以在创建 Cocktail
之后从 CocktailsDetailViewController
逐一设置。换句话说,按照 CoreDataBooks 示例,在添加和编辑情况下,ObjectID
何时何地从父 MOC 转换为 NSManagedObject
? -IPD
更新 - 这是添加方法:
- (IBAction)addCocktail:(id)sender {
CocktailsAddViewController *addViewController = [[CocktailsAddViewController alloc] init];
addViewController.title = @"Add Cocktail";
addViewController.delegate = self;
// Create a new managed object context for the new book -- set its persistent store coordinator to the same as that from the fetched results controller's context.
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
self.addingManagedObjectContext = addingContext;
[addingContext release];
[addingManagedObjectContext setPersistentStoreCoordinator:[[fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
Cocktail *newCocktail = (Cocktail *)[NSEntityDescription insertNewObjectForEntityForName:@"Cocktail" inManagedObjectContext:self.addingManagedObjectContext];
newCocktail.baseLiquor = (Ingredient *)[NSEntityDescription insertNewObjectForEntityForName:@"Ingredient" inManagedObjectContext:self.addingManagedObjectContext];
newCocktail.mixer = (Ingredient *)[NSEntityDescription insertNewObjectForEntityForName:@"Ingredient" inManagedObjectContext:self.addingManagedObjectContext];
newCocktail.volume = [NSNumber numberWithInt:0];
addViewController.cocktail = newCocktail;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addViewController];
[self.navigationController presentModalViewController:navController animated:YES];
[addViewController release];
[navController release];
}
这是 Brand
选择器中的崩溃位置(此 NSFetchedResultsController
由应用委托(delegate)的托管对象上下文支持:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
if ([delegate respondsToSelector:@selector(pickerViewController:didFinishWithBrand:forKeyPath:)])
{
[delegate pickerViewController:self
didFinishWithBrand:(Brand *)[fetchedResultsController objectAtIndexPath:indexPath]
forKeyPath:keyPath]; // 'keyPath' is @"baseLiquor.brand" in the crash
}
}
最后是委托(delegate)实现:
- (void)pickerViewController:(IngredientsPickerViewController *)pickerViewController
didFinishWithBrand:(Brand *)baseEntity
forKeyPath:(NSString *)keyPath
{
// set entity
[cocktail setValue:ingredient forKeyPath:keyPath];
// Save the changes.
NSError *error;
if (![cocktail.managedObjectContext save:&error]) {
// Update to handle the error appropriately.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1); // Fail
}
// dismiss picker
[self.navigationController popViewControllerAnimated:YES]
}
更多
我正在根据 Marcus 的建议取得进展——我将 addingManagedObjectContexts
映射到父 managedObjectContext 并将所有内容包装在 begin/endUndoGrouping
中以处理取消与保存.
但是,要创建的对象位于 NSFetchedResultsController
中,因此当用户点击“+”按钮添加 Cocktail
时,(可能是 - be-undone) 实体在呈现模态添加 View Controller 时短暂出现在 TableView 中。 MDN 示例是基于 Mac 的,因此它不涉及此 UI 行为。我该怎么做才能避免这种情况?
最佳答案
听起来您正在创建两个不同的核心数据堆栈(NSManagedObjectContext
、NSManagedObjectModel
和NSPersistentStoreCoordinator
)。从示例中您想要做的只是创建两个指向相同 NSPersistentStoreCoordinator
的 NSManagedObjectContext
实例。这将解决此问题。
将 NSManagedObjectContext
视为便笺本。您可以拥有任意数量,如果您在保存之前将其丢弃,则其中包含的数据将消失。但它们都保存到同一个地方。
更新
不幸的是,CoreDataBooks 是一个非常糟糕的例子。但是,对于您的问题,我建议删除 附加上下文的创建并查看是否发生错误。根据您发布的代码,我假设您直接从 Apple 的示例中复制了代码,双重上下文虽然几乎没有用,但应该可以正常工作。因此,我怀疑还有其他因素在起作用。
尝试使用单个上下文并查看问题是否仍然存在。您可能有其他一些有趣但微妙的错误导致您出现此错误;也许是某个地方或某些地方的过度释放。但第一步是删除双重上下文,看看会发生什么。
更新2
如果即使只有一个 MOC 也会崩溃,那么您的问题与上下文无关。单个 MOC 出现的错误是什么?当我们解决这个问题时,我们将解决您的整个问题。
至于更好的解决方案,请改用 NSUndoManager
。这就是它的设计目的。 Apple 真的不应该在他们的示例中推荐多个 MOC。
我最近在这里回答了一个关于将 NSUndoManager
与 Core Data 一起使用的问题,但您也可以查看我在 MDN 上的一些文章作为示例。
关于iphone - NS无效参数异常 : Illegal attempt to establish a relationship between objects in different contexts,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2416647/