NSInvalidArgumentException: незаконная попытка установить связь между объектами в разных контекстах - PullRequest
4 голосов
/ 10 марта 2010

У меня есть приложение, основанное на примере CoreDataBooks, которое использует addingManagedObjectContext для добавления Ingredient к Cocktail для отмены всего добавления. CocktailsDetailViewController, в свою очередь, вызывает BrandPickerViewController, чтобы (необязательно) установить название бренда для данного ингредиента. Cocktail, Ingredient и Brand - все NSManagedObjects. Cocktail требует, чтобы был установлен хотя бы один Ingredient (baseLiquor), поэтому я создаю его при создании Cocktail.

Если я добавлю Cocktail в CocktailsAddViewController : CocktailsDetailViewController (слияние с контекстом управляемого объекта Cocktail при сохранении) без установки baseLiquor.brand, то получится установить Brand из средства выбора (также сохраненного в управляемых коктейлях). контекст) позже от CocktailsDetailViewController.

Однако, если я попытаюсь установить baseLiquor.brand в CocktailsAddViewController, я получу:

Завершение приложения из-за необработанного исключения NSInvalidArgumentException, причина: «Незаконная попытка установить отношения «бренд» между объектами в разных контекстах '

С этот вопрос Я понимаю, что проблема в том, что Brand хранится в managedObjectContext приложения, а недавно добавленные Ingredient и Cocktail хранятся в addingManagedObjectContext, и что прохождение ObjectID вместо этого позволит избежать сбоя.

Чего я не понимаю, так это как реализовать средство выбора, чтобы все ингредиенты (baseLiquor, mixer, garnish и т. Д.) Могли быть установлены во время добавления, а также одно- по одному из CocktailsDetailViewController после создания Cocktail. Другими словами, следуя примеру CoreDataBooks, где и когда ObjectID будет превращено в NSManagedObject из родительского MOC в случаях добавления и редактирования? -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 поддерживается контекстом управляемого объекта делегата приложения:

- (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
    }
}

и, наконец, реализация делегата:

- (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]
}

ДАЖЕ БОЛЬШЕ

Я делаю прогы на основе предложений Маркуса - я сопоставил addingManagedObjectContexts с родительским managedObjectContext и завернул все в begin/endUndoGrouping для обработки отмены и сохранения.

Однако создаваемый объект находится в NSFetchedResultsController, поэтому, когда пользователь нажимает кнопку «+», чтобы добавить Cocktail, сущность (возможно, подлежащая отмене) кратко появляется в таблице. view в качестве модального контроллера. Пример MDN основан на Mac, поэтому он не затрагивает это поведение пользовательского интерфейса. Что я могу сделать, чтобы избежать этого?

Ответы [ 2 ]

6 голосов
/ 10 марта 2010

Похоже, вы создаете два разных стека базовых данных (NSManagedObjectContext, NSManagedObjectModel и NSPersistentStoreCoordinator). В этом примере вы просто создадите два экземпляра NSManagedObjectContext, указывающих на один и тот же NSPersistentStoreCoordinator. Это решит эту проблему.

Думайте о NSManagedObjectContext как о блокноте. Вы можете иметь столько, сколько хотите, и если вы выбросите его перед сохранением, данные, содержащиеся в нем, исчезнут. Но все они сохраняются в одном месте.

обновление

CoreDataBooks, к сожалению, действительно ужасный пример. Тем не менее, для вашей проблемы, я бы предложил удалить создание дополнительного контекста и посмотреть, происходит ли ошибка. Исходя из кода, который вы разместили, и я предполагаю, что код, который вы скопировали непосредственно из примера Apple, двойной контекст, хотя и практически бесполезный, должен работать нормально. Поэтому я подозреваю, что в игре что-то еще.

Попробуйте использовать один контекст и посмотрите, сохраняется ли проблема. У вас может быть другая интересная, но неуловимая ошибка, которая дает вам эту ошибку; возможно переиздание где-то или что-то в этом роде. Но первый шаг - удалить двойной контекст и посмотреть, что произойдет.

обновление 2

Если происходит сбой даже с одним MOC, тогда ваша проблема не имеет никакого отношения к контекстам. Какую ошибку вы получаете с одним MOC? Когда мы решим это, мы решим всю вашу проблему.

Что касается лучшего решения, используйте NSUndoManager. Вот для чего он предназначен. Apple REALLY не должна рекомендовать несколько MOC в их примере.

Я недавно ответил на вопрос об использовании NSUndoManager с Базовыми данными, но вы также можете посмотреть некоторые мои статьи о MDN для примера.

0 голосов
/ 30 декабря 2011

Да, вы определенно не хотите пересекать границы контекста при установке отношений между объектами; они оба должны быть в одном NSManagedObjectContext. В старом EOF были API для сброса объектов в разные контексты, но не похоже, что CoreData имеет аналог.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...