11

Core Data でネストされた管理対象オブジェクト コンテキストを使用するときに発生する問題の適切な解決策を見つけようと奮闘しています。Person と Name の 2 つのエンティティを持つモデルを考えてみましょう。各 Person は Name と 1 対 1 の関係にあり、Name の person 関係はオプションではありません。以前は、Person の-awakeFromInsertメソッドで、新しい Person の Name エンティティを自動的に作成していました。

- (void)awakeFromInsert
{
    [super awakeFromInsert];

    NSManagedObjectContext *context = [self managedObjectContext];
    self.name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context];
}

これは、ネストされていない単一の管理対象オブジェクト コンテキストで問題なく機能します。ただし、コンテキストに親コンテキストがある場合、子コンテキストが保存されると、新しい Person オブジェクトが親コンテキストで作成され、元の Person のプロパティと関係がコピーされる前に-awakeFromInsert、この新しいオブジェクトで再度呼び出されます。そのため、別の Name オブジェクトが作成され、既存の名前の関係がコピーされると「切断」されます。フローティング Name の now-nil関係の検証が失敗するため、保存は失敗します。この問題は、ここと他の場所で説明されています。person

これまでのところ、この問題に対する適切な解決策を思い付くことができませんでした。getter メソッドでリレーションシップを遅延作成すると、実際には同じ問題が発生します。これは、新しい Person が親コンテキストで作成されるときに、内部 Core Data 機構によって getter が呼び出されるためです。

私が思いつくことができる唯一のことは、自動関係生成を放棄し、Person を作成するコントローラー クラス、または+[Person insertNewPersonInManagedObjectContext:]私のコードによってのみ呼び出され、常に新しい Person オブジェクトを明示的に作成するために使用されるメソッド。おそらくこれが最善の解決策ですが、管理対象オブジェクトの作成に単一のメソッドのみを使用できるようにすることについて、それほど厳密にする必要はありません。簡単にチェック/除外、存在します。1 つには、複数の NSArrayController サブクラスが、管理対象オブジェクトの作成方法をカスタマイズすることを意味します。

この問題に遭遇した他の誰かが、1 つの NSManagedObject が作成/挿入時に関係オブジェクトを自動的に作成できるようにする洗練されたソリューションを思いつきましたか?

4

2 に答える 2

1

私は便利な方法の解決策に行きました。私のアプリのすべての NSManagedObject サブクラスには+insertInManagedObjectContext:メソッドがあります。これらのオブジェクトのインスタンスの作成 (自分のコードで) は、常にそのメソッドを使用して行われます。そのメソッド内で、私はこれを行います:

+ (instancetype)insertInManagedObjectContext:(NSManagedObjectContext *)moc
{
    MyManagedObject *result = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntityName" inManagedObjectContext:moc]
    [result awakeFromCreation];
    return result;
}

- (void)awakeFromCreation
{
    // Do here what used to be done in -awakeFromInsert.
    // Set up default relationships, etc.
}

NSArrayController の問題に関しては、それを解決することはまったく悪いことではありません。単に NSArrayController のサブクラスを作成し、overrode を作成し、-newObjectそのサブクラスをアプリ内の関連するすべての NSArrayControllers に使用しました。

@implementation ORSManagedObjectsArrayController

- (id)newObject
{
    NSManagedObjectContext *moc = [self managedObjectContext];
    NSEntityDescription *entity = [NSEntityDescription entityForName:[self entityName]
                                              inManagedObjectContext:moc];
    if (!entity) return nil;

    Class class = NSClassFromString([entity managedObjectClassName]);
    return [class insertInManagedObjectContext:moc];
}

@end
于 2013-03-15T17:21:50.580 に答える
1

頭に浮かぶ最初の考えは、Nameperson関係はオプションではありませんが、Personname関係もオプションではないということではありません。Personnoでを作成Nameし、コードで破棄して、Name後で実際に必要になったときに作成しても問題ありませんか?

そうでない場合、1 つの簡単な方法は、作成する前にルート コンテキストにいるかどうかを確認することですName

- (void)awakeFromInsert
{
    [super awakeFromInsert];

    NSManagedObjectContext *context = [self managedObjectContext];
    if ([context parentContext] != nil) {
        self.name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context];
    }
}

ただし、これは、常に子コンテキストで新しいインスタンスを作成し、コンテキストを1 レベル以上ネストしない場合にのみ機能します。

代わりに私がおそらく行うことはinsertNewPersonInManagedObjectContext:、あなたが説明したようなメソッドを作成することです。次に、次のようなものを追加して、インスタンスが作成されるすべてのケース (つまり、アレイ コントローラー) を処理します。

- (void)willSave
{
    if ([self name] == nil) {
        NSManagedObjectContext *context = [self managedObjectContext];
        Name *name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context];
        [self setName:name];
    }
}

...そしてもちろん、カスタムを気にしないでくださいawakeFromInsert...

于 2013-03-12T17:58:34.160 に答える