0

在熟悉核心数据时,我发现自己对尝试添加数据时传递各种视图控制器 (VC) 的问题感到困惑。

例如,在苹果提供的 CoreDataRecipes 项目作为示例 ( http://developer.apple.com/library/ios/#samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html ) 他们使用以下方法

当用户想要将配方添加到主表视图中显示的配方列表中并点击添加按钮时,主表视图控制器(称为 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];
}

这个新创建的对象(一个Recipe)被传递给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 之间来回传递食谱名称(字符串)?

我问是因为我很难理解何时在 segues 之间传递字符串、何时传递对象以及何时传递 managedObjectContexts ......

非常感谢任何指导,包括。任何指向讨论的设计理念的链接。

4

1 回答 1

1

您的问题是NSManagedObjects 没有上下文就无法生存。因此,如果您不将配方添加到上下文中,则必须将该配方的所有属性保存在“常规”实例变量中。当用户点击保存时,您可以从这些实例变量中创建一个配方。

这对于 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 中保存上下文!只需设置所有需要的配方属性,但不要管上下文。

在用户点击“取消”或“完成”后,调用此委托方法。要么保存editingContext和我们的上下文,要么什么都不做。

- (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:`
}
于 2013-07-16T08:10:45.407 回答