0

コア データに慣れるまでに、データを追加しようとするときにさまざまなビュー コントローラー (VC) に何を渡すかという問題に戸惑いました。

たとえば、Apple が例として提供する 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];
}

この新しく作成されたオブジェクト (レシピ) は、RecipeAddViewController に渡されます。RecipeAddViewController には、次のように、save と cancel の 2 つのメソッドがあります。

- (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 を渡すタイミングを理解するのに苦労しているため、質問しています...

どんなガイダンスも大歓迎です。問題となっているデザイン哲学の議論へのリンク。

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 にコンテキストを保存しないでください。Recipe のすべての必要な属性を設定するだけで、コンテキストはそのままにしておきます。

ユーザーが「キャンセル」または「完了」をタップした後、このデリゲート メソッドが呼び出されます。これは、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 に答える