NSInvalidArgumentException:非法尝试在不同上下文中的对象之间建立关系

iPh*_*ire 4 iphone core-data objective-c

我有一个基于CoreDataBooks示例的应用程序,它使用一个addingManagedObjectContext来添加Ingredient到a Cocktail以撤消整个添加.在CocktailsDetailViewController随后调用BrandPickerViewController为给定的成分(可选)设置一个品牌名称.Cocktail,Ingredient并且Brand都是NSManagedObjects.Cocktail需要至少设置一个Ingredient(baseLiquor),所以我在创建时创建它Cocktail.

如果我CocktailCocktailsAddViewController : CocktailsDetailViewController没有设置的情况下添加in (在保存时合并到Cocktail托管对象上下文中)baseLiquor.brand,那么它可以Brand在稍后从一个选择器(也存储在Cocktails托管上下文中)设置CocktailsDetailViewController.

但是,如果我尝试设置baseLiquor.brandCocktailsAddViewController,我得到:

由于未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因是:'非法尝试在不同上下文中的对象之间建立关系'品牌'

这个问题我明白问题是Brand存储在应用程序中managedObjectContext并且新添加IngredientCocktail存储在其中addingManagedObjectContext,而传递它ObjectID会避免崩溃.

我不明白的是如何实现的选择器一般让所有的成份(的baseLiquor,mixer,garnish等)可附加期间设置,以及一个接一个从CocktailsDetailViewControllerCocktail已创建.换句话说,在CoreDataBooks示例之后,在添加和编辑的情况下,何时何地将从父MOC ObjectID变为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];

}
Run Code Online (Sandbox Code Playgroud)

这里是Brand选择器中崩溃的站点(这NSFetchedResultsController是由app 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
    }
}
Run Code Online (Sandbox Code Playgroud)

最后代表实施:

- (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]
}
Run Code Online (Sandbox Code Playgroud)

我根据Marcus的建议制作了progess - 我将其映射addingManagedObjectContexts到父的managedObjectContext并将所有内容包装起来begin/endUndoGrouping以处理取消与保存.

但是,要创建的对象在a中NSFetchedResultsController,因此当用户点击"+"按钮添加时Cocktail,(可能要撤消的)实体会短暂出现在表视图中,因为模式添加视图控制器已呈现.MDN示例是基于Mac的,因此它不会触及此UI行为.我该怎么做才能避免这种情况?

Mar*_*rra 6

听起来你正在创建两个不同的核心数据堆栈(NSManagedObjectContext,NSManagedObjectModel,和NSPersistentStoreCoordinator).你想从这个例子中做的只是创建NSManagedObjectContext指向相同的两个实例NSPersistentStoreCoordinator.这将解决这个问题.

把它想象NSManagedObjectContext成一个便笺簿.您可以拥有任意数量的内容,如果在保存之前丢弃它,其中包含的数据就会消失.但他们都保存到同一个地方.

更新

不幸的是,CoreDataBooks是一个非常可怕的例子.但是,对于您的问题,我建议删除附加上下文的创建,并查看是否发生错误.基于您发布的代码,我假设您直接从Apple的示例中复制的代码,双重上下文,虽然几乎没用,应该可以正常工作.所以我怀疑还有别的东西在起作用.

尝试使用单个上下文,看看问题是否仍然存在.你可能有一些其他有趣但微妙的错误,它会给你这个错误; 也许是某个地方的过度释放或沿着那些方向的东西.但第一步是删除双重上下文,看看会发生什么.

更新2

如果即使使用单个MOC也会崩溃,那么您的问题与上下文无关.单个MOC的错误是什么?当我们解决这个问题时,我们将解决您的整个问题.

至于更好的解决方案,请NSUndoManager改用.这就是它的设计目标.Apple 真的不应该在他们的例子中推荐多个MOC.

我最近在这里回答了一个关于使用NSUndoManager核心数据的问题,但你也可以在MDN上看一些我的文章作为例子.