0

私は以下を機能させようとしています。

APIからフェッチされたデータをテーブルビューに表示しているテーブルビューがあります。その目的のために、私はNSFetchedResultsControllerを使用しています。

self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                        managedObjectContext:self.database.managedObjectContext
                                                                      sectionNameKeyPath:nil
                                                                               cacheName:nil];

次のようなバックグラウンドコンテキストでエンティティを作成します。

    NSManagedObjectContext *backgroundContext;
    backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    backgroundContext.parentContext = document.managedObjectContext; 

    [backgroundContext performBlock:^{
        [MyAPI createEntitiesInContext:backgroundContext];

        NSError *error = nil;
        [backgroundContext save:&error];
        if (error) NSLog(@"error: %@",error.localizedDescription);

        [document.managedObjectContext performBlock:^{
            [document updateChangeCount:UIDocumentChangeDone];
            [document.managedObjectContext save:nil];
        }];

これで、新しいデータを取得する(および上記のようなエンティティを挿入/更新する)たびに、NSFetchedResultsControllerが正常に機能しなくなります。特に、私は常に1つのエンティティを更新しています(新しいエンティティを作成していません)が、テーブルビューには2つのエンティティが表示されます。アプリを再起動すると、正しく表示されます。

self.database.managedObjectContextでエンティティの作成([MyAPI createEntities])を実行すると、すべてが正常に機能します。

私が間違っていることについて何か考えはありますか?ここでSOの既存のスレッドを見ると、私はそれを正しい方法で行っていると思います。繰り返しますが、バックグラウンドコンテキスト(ただしdocument.managedObjectContext)でコアデータの保存を行わない場合は、正常に機能します...

4

3 に答える 3

1

今日、Appledevフォーラムで同様の問題について読みました。おそらくこれはあなたと同じ問題であり、https://devforums.apple.com/message/666492#666492、その場合はおそらくバグがあります(または少なくとも同じ問題を抱えている他の誰かと話し合ってください!)。

そうではないと仮定すると、ネストされたコンテキストでやりたいことが完全に可能であるように思われるため、。でバグがないと仮定しUIManagedDocumentます。

私の唯一の予約は、バッチ読み込みを機能させようとしているUIManagedDocumentことですが、ネストされたコンテキストでは機能しないようです(https://stackoverflow.com/q/11274412/1347502)。NSFetchedResultsControllerの主な利点の1つは、バッチロードによってパフォーマンスを向上させることができることだと思います。したがって、これを実行できない場合は、で使用する準備ができていない可能性がありますが、私はまだその問題の根底に到達していませUIManagedDocumentん。NSFetchedResultsControllerUIManagedDocument

その予約はさておき、ネストされたコンテキストとバックグラウンド作業について私が読んだり見たりしたほとんどの命令は、ピアの子コンテキストで行われているようです。説明したのは、親、子、孫の構成です。WWDC 2012ビデオ「セッション214-コアデータのベストプラクティス」(+ 16:00分)では、Appleは、このシナリオの親コンテキストに別のピアコンテキストを追加することを推奨しています。

backgroundContext.parentContext = document.managedObjectContext.parentContext;

作業はこのコンテキストで非同期に実行され、バックグラウンドコンテキストを保存するための呼び出しを介して親にプッシュされます。次に、親は非同期で保存され、ピアコンテキスト(この場合はdocument.managedObjectContext)は、フェッチ、マージ、または更新を介して変更にアクセスします。これは、UIManagedDocumentドキュメントにも記載されています。

  • 必要に応じて、バックグラウンドスレッドから親コンテキストに直接データをロードできます。parentContextを使用して親コンテキストを取得できます。親コンテキストにデータをロードするということは、子コンテキストの操作を混乱させないことを意味します。フェッチを実行するだけで、バックグラウンドでロードされたデータを取得できます。

[編集:これを読み直すと、Jefferyの提案を推奨している可能性があります。つまり、新しいコンテキストをまったく作成せず、親コンテキストを使用しているだけです。]

そうは言っても、ドキュメントは、通常、子コンテキストでsaveを呼び出すのではなく、UIManagedDocument'ssaveメソッドを使用することを示唆しています。これは、saveを呼び出すか、問題の一部である可能性があります。Jefferyが述べたように、親コンテキストでsaveを呼び出すことはより強く推奨されません。スタックオーバーフローについて読んだ別の回答は、保存updateChangeCountをトリガーするためにのみ使用することをお勧めします。UIManagedDocumentしかし、私はAppleから何も読んでいないので、おそらくこの場合、UIManagedDocument saveToURL:forSaveOperation:completionHandler:メソッドを呼び出すことは、すべてを同期して保存するのに適切でしょう。

次の明らかな問題は、変更が発生したことをNSFetchedResultsControllerに通知する方法だと思います。上記のようにセットアップを簡略化してから、さまざまなNSManagedObjectContextObjectsDidChangeNotificationコンテキストでさまざまな通知または保存通知をサブスクライブし、UIMangedDocument保存、自動保存、またはバックグラウンドの変更が親に保存されるときに呼び出される通知がある場合は、それを確認したいと思います(この場合は許容されます)。NSFetchedResultsController基になるデータとの同期を維持するために、はこれらの通知に接続されていると思います。

あるいは、メインコンテキストでフェッチ、マージ、または更新を手動で実行して変更をプルスルーし、NSFetchedResultsController更新が必要であることを何らかの方法で通知する必要がありますか?

個人的には、一般消費の準備ができているかどうか疑問に思っていますUIManagedDocumentが、今年のWWDCではそれについての言及はなく、代わりに、はるかに複雑なソリューションを構築する方法についての長い議論が行われました:「セッション227-コアデータでのiCloudの使用」

于 2012-07-06T14:12:18.113 に答える
1

サーバーからデータをフェッチするメソッドでは、最初にエンティティを作成し、その後、次の2つのメソッドを呼び出してドキュメントへの変更を保存します。

[self.managedObjectContext performBlock:^{
     // create my entities


     [self.document updateChangeCount:UIDocumentChangeDone];
     [self.document savePresentedItemChangesWithCompletionHandler:^(NSError *errorOrNil) {
            ...
      }];
}];
于 2013-11-26T10:22:53.160 に答える
0

[self.fetchedResultsController performFetch:&error]別のコンテキストで結果を更新しているため、ビューコントローラー-viewWillAppear:メソッドを呼び出す必要があると思います。


更新後

[backgroundContext save:&error]OK、またはを呼び出すべきではありません[document.managedObjectContext save:nil]。参照:UIManagedDocumentクラスリファレンス

通常、標準のUIDocumentメソッドを使用してドキュメントを保存する必要があります。子コンテキストを直接保存する場合は、変更を親コンテキストにのみコミットし、ドキュメントストアにはコミットしません。親コンテキストを直接保存すると、ドキュメントが実行する他の重要な操作を回避できます。

コンテキストで作成された新しいオブジェクトを使用-insertedObjectsobtainPermanentIDsForObjects:error:て永続化する必要がありました。

次に、バックグラウンドで実行するために新しいコンテキストを作成する必要はないと思います。document.managedObjectContext.parentContextで更新を実行するために利用可能なバックグラウンドコンテキストである必要があります。

最後に、私は[document updateChangeCount:UIDocumentChangeDone]あまり頻繁に電話をかけません。これは、ドキュメントによって自動的に処理されます。いつでも好きなときにそれを行うことができますが、それは必要ではないはずです。

これが私があなたのメソッドを呼び出す-createEntitiesInContext方法です。

[document.managedObjectContext.parentContext performBlock:^{
    [MyAPI createEntitiesInContext:document.managedObjectContext.parentContext];

    NSSet *objects = [document.managedObjectContext.parentContext insertedObjects];
    if (objects.count > 0) {
        NSError *error = nil;
        [document.managedObjectContext.parentContext obtainPermanentIDsForObjects:objects error:&error]
        if (error) NSLog(@"error: %@",error.localizedDescription);
    }
}];
于 2012-07-05T20:37:28.197 に答える