2013-07-16 2 views
0

, знакомясь с основными данными, я обнаружил, что меня озадачил вопрос о том, как передавать различные контроллеры представлений (VC) при попытке добавить данные., добавляя объект Core Data от segue

, например, в проекте CoreDataRecipes, что яблоко обеспечивает как пример (http://developer.apple.com/library/ios/#samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html) они используют следующий подход

, когда пользователь хочет добавить рецепт в список рецептов, представленных в представлении основной таблицы и нажмет кнопку Add, мастер-контроллер таблицы (так называемый RecipeListTableViewController) создает новый управляемый объект (рецепт) следующим образом:

- (void)add:(id)sender { 
// To add a new recipe, create a RecipeAddViewController. Present it as a modal view so that the user's focus is on the task of adding the recipe; wrap the controller in a navigation controller to provide a navigation bar for the Done and Save buttons (added by the RecipeAddViewController in its viewDidLoad method). 
RecipeAddViewController *addController = [[RecipeAddViewController alloc] initWithNibName:@"RecipeAddView" bundle:nil]; 
addController.delegate = self; 

Recipe *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:@"Recipe" inManagedObjectContext:self.managedObjectContext]; 
addController.recipe = newRecipe; 

UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:addController]; 
[self presentModalViewController:navigationController animated:YES]; 

[navigationController release]; 
[addController release]; 
} 

этот вновь созданный объект (Рецепт) передается в RecipeAddViewController. RecipeAddViewController имеет два метода, сохранить и отменить, следующим образом:

- (void)save { 

recipe.name = nameTextField.text; 

NSError *error = nil; 
if (![recipe.managedObjectContext save:&error]) { 
    /* 
    Replace this implementation with code to handle the error appropriately. 

    abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button. 
    */ 
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
    abort(); 
}  

[self.delegate recipeAddViewController:self didAddRecipe:recipe]; 

}

- (void)cancel { 

[recipe.managedObjectContext deleteObject:recipe]; 

NSError *error = nil; 
if (![recipe.managedObjectContext save:&error]) { 
    /* 
    Replace this implementation with code to handle the error appropriately. 

    abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button. 
    */ 
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
    abort(); 
}  

[self.delegate recipeAddViewController:self didAddRecipe:nil]; 

}

я озадачен этим дизайнерским подходом. почему RecipeListViewController создает объект, прежде чем мы узнаем, хочет ли пользователь фактически ввести новое имя рецепта и сохранить новый объект? почему бы не передать managedObjectContext в addRecipeController и дождаться, пока пользователь не ударит, чтобы создать объект и заполнить его поля данными? это позволяет избежать необходимости удаления нового объекта, если новый рецепт для добавления не существует. или почему бы просто не передать имя рецепта (строку) назад и вперед между RecipeListViewController и RecipeAddController?

я спрашиваю, потому что я борюсь, чтобы понять, когда передавать строки между перетекает, когда передавать объекты, и когда передать managedObjectContexts ...

любое руководство высоко ценится, в том числе. любые ссылки на обсуждение проблем философии дизайна.

ответ

1

Ваша проблема в том, что NSManagedObject s не может жить без контекста. Поэтому, если вы не добавляете рецепт в контекст, вам нужно сохранить все атрибуты этого рецепта в «обычных» переменных экземпляра. И когда пользовательские краны сохраняют, вы создаете Рецепт из этих переменных экземпляра.

Это не большая проблема для AddViewController, но какой viewController вы хотите использовать для редактирования рецепта? Возможно, вы можете повторно использовать свой AddViewController. Но если вы сохраните все данные как переменные экземпляра, он станет немного уродливым, потому что сначала вы должны получить все данные из Рецепта, сохраните его в переменных экземпляра, и когда вы закончите, вам нужно сделать обратное.

Вот почему я обычно использую другой подход. Я использую контекст редактирования для редактирования (или добавление, которое в основном просто редактирует).

- (void)presentRecipeEditorForRecipe:(MBRecipe *)recipe { 
    NSManagedObjectContext *editingContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; 
    editingContext.parentContext = self.managedObjectContext; 
    MBRecipe *recipeForEditing; 
    if (recipe) { 
     // get same recipe inside of the editing context. 
     recipeForEditing = (MBRecipe *)[editingContext objectWithID:[recipe objectID]]; 
     NSParameterAssert(recipeForEditing); 
    }  
    else { 
     // no recipe for editing. create new one 
     recipeForEditing = [MBRecipe insertInManagedObjectContext:editingContext]; 
    } 

    // present editing view controller and set recipeForEditing and delegate 
} 

Довольно простой код. Он создает новый детский контекст, который используется для редактирования. И получает рецепт для редактирования из этого контекста.

Вы должны не сохранить контекст в своем EditViewController! Просто установите все нужные атрибуты рецепта, но оставьте контекст в покое.

После того, как пользователь нажал «Отмена» или «Готово», вызывается этот метод делегата. Что либо сохраняет редактированиеКонтекст и наш контекст, либо ничего не делает.

- (void)recipeEditViewController:(MBRecipeEditViewController *)editViewController didFinishWithSave:(BOOL)didSave { 
    NSManagedObjectContext *editingContext = editViewController.managedObjectContext; 
    if (didSave) { 
     NSError *error; 
     // save editingContext. this will put the changes into self.managedObjectContext 
     if (![editingContext save:&error]) { 
      NSLog(@"Couldn't save editing context %@", error); 
      abort(); 
     } 

     // save again to save changes to disk 
     if (![self.managedObjectContext save:&error]) { 
      NSLog(@"Couldn't save parent context %@", error); 
      abort(); 
     } 
    } 
    else { 
     // do nothing. the changes will disappear when the editingContext gets deallocated 
    } 
    [self dismissViewControllerAnimated:YES completion:nil]; 
    // reload your UI in `viewWillAppear:` 
} 
+0

благодарит за ваш ответ. это кажется очень изящным и позволяет избежать того, что меня беспокоит, а именно, что прерывание процесса addRecipe может привести к тому, что в моем управляемом объектеObjectContext останется пустая запись. У меня есть еще несколько вопросов относительно подхода. во-первых, я прочитал о типах параллелизма и мне интересно, почему вы выбираете NSMainQueueConcurrencyType для контекста редактирования. во-вторых, почему NSParameterAssert (recipeForEditing); нужно? –

+0

Обновление: когда я пытаюсь использовать подход Matthias B., я получаю следующую ошибку во время выполнения: *** Завершение приложения из-за неперехваченного исключения «NSInvalidArgumentException», причина: «Родительский NSManagedObjectContext должен использовать либо NSPrivateQueueConcurrencyType, либо NSMainQueueConcurrencyType». ... возникает вопрос, когда я объявляю исходный NSManagedObjectContext в делегате приложения, мне нужно сделать его одним из этих типов, и если да, то что лучше? –