23

NSFetchedResultsController介して別のスレッドで管理対象オブジェクトを更新する操作がいくつかありますNSOperationQueue

FRC(述語付き)は次のようになります。

- (NSFetchedResultsController*)fetchedResultsController
{
    if(fetchedResultsController) return fetchedResultsController;

    NSManagedObjectContext* mainContext = [self managedObjectContext];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [fetchRequest setEntity:[NSEntityDescription entityForName:@"Check" inManagedObjectContext:mainContext]];
    [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"isSync == %@", [NSNumber numberWithBool:NO]]];
    [fetchRequest setFetchBatchSize:10];

    fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
    fetchedResultsController.delegate = self;

    [fetchRequest release], fetchRequest = nil;

    return fetchedResultsController;
}

メインスレッドとスレッド化された操作には、独自の管理対象オブジェクトコンテキストがあります。彼らは同じコーディネーターを共有するだけです。

スレッド化された操作内で、isSyncプロパティをからNOに変更しますYES。更新するエンティティを知るためCheckに、メインコンテキストはスレッド化されたエンティティに渡されますNSManagedObjectID。スレッド化された操作は、次のように管理対象オブジェクトを取得します。

-(void)main
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSManagedObjectContext *exportContext = [[NSManagedObjectContext alloc] init];
    [exportContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];

    //...

    Check* check = (Check*)[exportContext existingObjectWithID:objID error:&error];
    check.isSync = [NSNumber numberWithBool:YES];

    //...

    [exportContext save:&error];

    [pool release], pool = nil;
}

スレッド操作が通知を呼び出すとsavemergeChangesFromContextDidSaveNotification通知が呼び出され、メインコンテキストが変更をマージします。

- (void)contextChanged:(NSNotification*)notification
{
    if ([notification object] == [self managedObjectContext]) return;

    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
        return;
    }

    [[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}

リードの説明をログに記録しnotificationて、変更が正しく実行されていることを確認します。

私の問題

のデリゲートメソッドはNSFetchedResultsControllerDelegate呼び出されません。

同じコンテキスト(メインコンテキスト)を処理すると、変更をリッスンでき、デリゲートメソッドが呼び出されるため、これは非常に奇妙です。たとえば、の行オブジェクトを削除しますUITableView

同じ問題のあるSOに関するトピックをいくつか見つけました。私はすべての回避策を試しましたが、価値のある解決策を見つけることができません:

  1. NSFetchedResultsControllerが他のコンテキストからの更新を表示しない

  2. バックグラウンドスレッドからの更新をマージした後、NSFetchedResultsControllerがデリゲートメソッドを起動しません

  3. 述部を持つNSFetchedResultsControllerは、異なるNSManagedObjectContextからマージされた変更を無視します

前もって感謝します。

編集

上記のコードは以前のモデルで機能していました。次に、前のモデルからエンティティをコピー(および貼り付け)する新しいモデルを作成しましたが、現在は機能しません。

提案?

編集2

これは私がNSFetchedResultsControllergetterで使用している述語です。それは私のせいですが、私が投稿を書いたとき、私はそれをコピーしませんでした。

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"insertionDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

// previous code here
[fetchRequest setSortDescriptors:sortDescriptors];

さて、ジョディについて最後のコメント

NSOperationのmain()で、新しいオブジェクトをロードしています。そこでは、新しいオブジェクトごとにisSyncをYESに設定しているように見えます。fetchedResultsControllerに使用する述語は、isSync==NOを持つオブジェクトのみを検索します。

プロパティisSyncがYESに設定されている場合NSFetchedResultsController、述語に一致しない行が変更および削除されることを監視します。私が間違っている?

バックグラウンドからメインスレッドへの変更をマージするときに、isSyncプロパティを更新したオブジェクトがほとんどないことがわかります。

4

2 に答える 2

11

あなたは基本的な考えを持っているので、おそらくあなたのコードのどこかにバグがあります...

バックグラウンドMOCから通知を受信するには、正しく登録していることを再確認してください。

すべてのオブジェクトからすべての通知を受信するように登録します。そのメソッドで、イベントとそのすべてのデータをログに記録します。オブジェクトがMOCの場合、そのすべてのプロパティ(特に、登録、挿入、更新、および削除されたオブジェクトのリスト)をダンプします。

保存呼び出しの直前と直後、および通知をマージするための通知ハンドラーにログステートメントを配置します。

また、多くのコードを省略したため、実際に何をしているのかわかりませんが、含まれているコードサンプルでは、​​読み込まれるすべてのオブジェクトに対してisSyncをYESに設定するのは難しいようですが、フェッチリクエストではisSyncが設定されているオブジェクトのみが必要ですいいえに。これらの新しいオブジェクトはいずれも、その述語を渡しません。

最後に、モデル定義を再確認し、正しい数値タイプを使用していることを確認してください。これは大きな問題の原因となる可能性があります。

編集

そうそう、忘れました...フェッチリクエストにソート記述子がありません。FRCを作成する場合、フェッチ要求には少なくとも1つのソート記述子が含まれている必要があります...複数のセクションがある場合は、最初のソート記述子を使用してオブジェクトをセクションにグループ化します。

Alexsanderのコメントをフォローアップするために...投稿の冒頭でほのめかしましたが、MOCがあなたのコメントとしてよく知られている場合を除いて、MOCからの通知を聞きたくないことは確かです(もちろん、あなたがデバッグ目的でログを記録するだけです)。使用しているMOCについて知っておく必要があります。

さらに、このタイプの処理には親/子MOCを使用することをお勧めしますが、適切に実行すれば、実行していることは機能するはずです。

親(プライベート同時実行タイプ)メイン(メイン同時実行タイプ)

次に、バックグラウンドMOCを使用して、メインMocを親として設定してもらいます。保存すると、オブジェクトはメインMOCに直接挿入されます。その後、メインMOCは、後で保存を発行してディスクに配置できます。

または、バックグラウンドMOCを「親」にペアレント化してから、「メイン」MOCがフェッチを再発行して、親からデータを取得することもできます。

于 2012-05-07T18:25:22.140 に答える
2

私はちょうど同じ問題を抱えていましたが、それは親/子のコンテキストで解決しました。これが私が抱えていた問題です。

独自のmanagedObjectContext(必須の)バックグラウンドスレッドでCore Dataオブジェクトグラフを更新していましfetchedResultsControllerたが、データベースに加えられた変更を取得できませんでした。

私がそれを解決した後、私はいくつかのメモを取りました:

ManagedObjectContextsはスレッドセーフではありません。つまり、managedObjectContextを他のスレッドと共有することはできません。スレッドがを使用する必要がある場合、スレッドはmanagedObjectContextそれ自体のmanagedObjectContextを初期化します。

初期化するmanagedObjectContextには、次の2つの方法があります。

  • alloc/init次に、そのpersistentStoreCoordinatorプロパティを設定します

  • alloc/init次に、parentContextプロパティの代わりにプロパティをpersistentStoreCoordinator設定します

注:persistentStoreCoordinatorとの両方のparentContextプロパティを設定することはできませんmanagedObjectContext

親/子コンテキストの使用は、コンテキストが「メインスレッドでのみ使用する必要があるコントローラーおよびUIオブジェクトにリンクされている」バックグラウンドスレッドで実行される場合に必要です(コアデータドキュメント)。

親/子コンテキストに必要な要件は次のとおりです。

  • 親コンテキストはメインスレッドに存在します

  • 子コンテキストはバックグラウンドスレッドに存在します

  • 両方のコンテキストをメソッドで初期化する必要がありますinitWithConcurrencyType:NSMainQueueConcurrencyType

  • 変更のバッチが子コンテキストで実行された場合、両方のコンテキストで保存操作を実行する必要があります。これらの保存操作は、performBlockメソッドにネストする必要があります。

    childContext performBlock:^{
        [childContext save:nil];
        [self.parentContext performBlock:^{
            [self.parentContext save:nil];                
        }];
    }];
    

編集:上記のコードは、2つの理由から実際には悪い考えです:

1)親コンテキストを保存せずに機能します。

2)親スレッドが実行されている場合、メインスレッドはブロックされます。

お役に立てば幸いです。

編集:これは私を大いに助けたstackoverflowスレッドです:コアデータの親ManagedObjectContextは子コンテキストと並行性タイプを共有する必要がありますか?

于 2012-07-23T18:51:03.273 に答える