2

ドキュメントベースのCoreDataアプリケーション(Mac OS X 10.5以降で実行)がありNSManagedObjectContext、メインスレッドで2つを使用しようとしています。セカンダリコンテキストで行われた変更をメイン(プライマリ)コンテキストにマージしたいと思います。さらに、セカンダリコンテキストからマージされた変更を元に戻して、ドキュメントに「ダーティ」のマークを付ける必要があります。私の質問は「メインスレッドから実行されるコアデータの挿入を元に戻す」に似ていると思いますが、ATMでは別のスレッドを使用していません。

私はNSManagedObjectContextDidSaveNotification次のように(呼び出し時に2番目のコンテキストから送信される)を観察してきました-[self.secondaryContext save:]

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(mocDidSave:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:self.secondaryContext];

-mocDidSave:オブザーバーによって呼び出されたメソッドでは、プライマリコンテキストで使用-[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:]して、セカンダリコンテキストからプライマリコンテキストへの変更をマージしようとしました。

- (void)mocDidSave:(NSNotification *)notification
{
    [self.primaryContext mergeChangesFromContextDidSaveNotification:notification];
}

ただし、たとえば、挿入されたオブジェクトはアレイコントローラにすぐに表示されますが、ドキュメントはダーティとしてマークされておらずisInserted、新しく追加された管理対象オブジェクトのプロパティはYESに設定されていません。また、(プライマリコンテキストへの)挿入は元に戻せません。

挿入されたオブジェクトを再フォールトすると、少なくともドキュメントがダーティとしてマークされますが、挿入は元に戻せません。

- (void)mocDidSave:(NSNotification *)notification
{
    [self.primaryContext mergeChangesFromContextDidSaveNotification:notification];

    for (NSManagedObject *insertedObject in [[notification userInfo] objectForKey:NSInsertedObjectsKey]) {
        [self.primaryContext refreshObject:[self.primaryContext existingObjectWithID:[insertedObject objectID] error:NULL] mergeChanges:NO];
    }
}

Wrt -mocDidSave:、カスタム実装で少し良い結果が得られました:

- (void)mocDidSave:(NSNotification *)notification
{
    NSDictionary *userInfo = [notification userInfo];

    NSSet *insertedObjects = [userInfo objectForKey:NSInsertedObjectsKey];
    if ([insertedObjects count]) {
        NSMutableArray *newObjects = [NSMutableArray array];
        NSManagedObject *newObject = nil;
        for (NSManagedObject *insertedObject in insertedObjects) {
            newObject = [self.primaryContext existingObjectWithID:[insertedObject objectID] error:NULL];
            if (newObject) {
                [self.primaryContext insertObject:newObject];
                [newObjects addObject:newObject];
            }
        }

        [self.primaryContext processPendingChanges];

        for (NSManagedObject *newObject in newObjects) {
            [self.primaryContext refreshObject:newObject mergeChanges:NO];
        }
    }

    NSSet *updatedObjects = [userInfo objectForKey:NSUpdatedObjectsKey];
    if ([updatedObjects count]) {
        NSManagedObject *staleObject = nil;
        for (NSManagedObject *updatedObject in updatedObjects) {
            staleObject = [self.primaryContext objectRegisteredForID:[updatedObject objectID]];
            if (staleObject) {
                [self.primaryContext refreshObject:staleObject mergeChanges:NO];
            }
        }
    }

    NSSet *deletedObjects = [userInfo objectForKey:NSDeletedObjectsKey];
    if ([deletedObjects count]) {
        NSManagedObject *staleObject = nil;
        for (NSManagedObject *deletedObject in deletedObjects) {
            staleObject = [self.primaryContext objectRegisteredForID:[deletedObject objectID]];
            if (staleObject) {
                [self.primaryContext deleteObject:staleObject];
            }
        }

        [self.primaryContext processPendingChanges];
    }
}

これにより、アレイコントローラが更新され、ドキュメントがダーティとしてマークされ、挿入と削除が元に戻されます。ただし、まだ元に戻せない更新については、まだ問題があります。代わりに、すべてのupdatedObjectsを手動でループし-[NSManagedObject changedValues]、プライマリコンテキストで変更を再適用するために使用する必要がありますか?

もちろん、このカスタム実装は、メインコンテキストのセカンダリコンテキストから多くの作業を複製します。マージを元に戻すことができるステップとして維持しながら、2つのコンテキスト間のマージを取得する他の/より良い方法はありますか?

4

1 に答える 1

1

個別のスレッドを使用していない場合は、実際にコンテキストを分離する必要はありません。同じスレッドで 2 つのコンテキストを使用すると、何も得られずに複雑さが増します。スレッドを使用するかどうかわからない場合は、1 つのコンテキストのみを使用することを強くお勧めします。時期尚早の最適化は諸悪の根源です。

保存すると元に戻すマネージャーがリセットされるため、元に戻すことがNSManagedObjectContextDidSaveNotificationできる操作を実行するために使用することはできません。ご存知のように、アプリをだましてドキュメントが汚れていると思わせることはできますが、元に戻すマネージャーに前回の保存を過ぎたことを強制することはできません。

無制限に元に戻す唯一の方法は、ドキュメントの複数のバージョンをバックグラウンドで保存することです。IIRC では、元に戻すマネージャーをシリアル化して、ファイルに書き込んでバックトラックにリロードできるようにすることもできます。

于 2011-07-01T16:32:57.820 に答える