2

UIManagedDocument使用してリストに表示するデータがありますNSFetchedResultsController。データはバックグラウンドで定期的に更新され、変更はUIManagedDocument.managedObjectContext(performBlock: を使用して) に配置されます。

ドキュメントのメイン コンテキストからデータを表示すると、すべてが期待どおりに機能します。しかし、メイン コンテキスト ( child.parentContext = document.managedObjectContext) の子であるコンテキストでリストを表示するとすぐに、オブジェクトが表示されず、次のエラーがコンソールに出力されます。

foo[17895:15203] CoreData: error: (NSFetchedResultsController)
                 The fetched object at index 5 has an out of order section name 'E.
                 Objects must be sorted by section name'

これは、新しいオブジェクトがドキュメント コンタクトに挿入された後にのみ発生します。自動保存が行われるまでしばらく待つと、リストが正常に表示されます。また、問題は、子コンテキストのみでsectionNameKeyPathセットを持っている場合のみです。NSFetchedResultsController

これは、フェッチされた結果コントローラーをセットアップする方法です。派手なことは何もないので、ここで何が間違っているのかわかりません。

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Contact"];
fetchRequest.sortDescriptors = [Contact userDefinedSortDescriptors];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"hidden == nil || hidden == NO"];

NSFetchedResultsController *fetchedResultsController = 
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                    managedObjectContext:_managedObjectContext
                                      sectionNameKeyPath:[Contact userDefinedSectionNameKeyPath]
                                               cacheName:@"ContactList"];

[Contact userDefinedSortDescriptors]実行時に[Contact userDefinedSectionNameKeyPath]解決されます。ソート記述子には、最初のエントリとして sectionNameKeyPath が含まれています。どちらも、nilまたは他の面白いものにすることはできません。

編集:いくつかのあいまいな部分を明確にしました。具体的には、ドキュメント管理オブジェクト コンテキストで -save: を呼び出しません。

編集 2 : MOC が互いにどのように関係しているかを説明しようと思います。

3 つの管理対象オブジェクト コンテキストが使用されます。

1)UIManagedDocument.managedObjectContextドキュメントを読み込んで作成されます。

2) 時々オブジェクトを更新するバックグラウンド スレッドが実行されています。これはドキュメント MOC を親とするプライベート キュー moc です。

context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.parentContext = repository.managedObjectContext;

[context performBlock:^{ /* updates */ }];
[context performBlock:^{ [context save:NULL]; }];

3) ユーザーが変更を加えたい場合、新しい MOC がドキュメント MOC の子として作成されます。これはメイン キューの MOC です。これは、上記のフェッチを実行するために使用されるコンテキストです。

バックグラウンド更新は NSOperationQueue から行われますが、バックグラウンド MOC へのすべてのアクセスは . で適切に囲まれてい-performBlock:ます。他のすべてのアクセスは、メイン スレッドから行われます。

編集 3 : いくつかの設定で遊んでいるNSFetchRequestときに、設定すると問題が解決することがわかりましたfetchRequest.includesPendingChanges = NO。しかし、UIManagedDocument によって変更がバックグラウンドで保存されるまで、ユーザーには更新が表示されないため、これは実行可能なソリューションではありません。

4

1 に答える 1

4

ここで私にいくつかの危険信号があります。まずは、これ…

データはバックグラウンドで定期的に更新され、変更は UIManagedDocument.managedObjectContext に保存されます (performBlock: を使用)。

UIManagedDocuments は自動保存を実装しており、保存メソッドを直接呼び出す必要があるのは最初の作成時のみです。他のすべての「保存」は、自動保存インターフェースを介して行う必要があります。基本的に、それは呼び出します

[document updateChangeCount:UIDocumentChangeDone];

UndoManager を使用する場合、これは自動的に行われます。そのため、document.managedContext に直接フィードする場合は、上記のメソッドを呼び出して、変更が完了したことをマネージド ドキュメントに通知するだけで、他のすべては自動的に処理されます。

第二に、これが何を意味するのかわかりません:

しかし、子コンテキストでリストを表示するとすぐに、

何の子コンテキスト?任意のコンテキストが何かを見る必要があるのはなぜですか? UIManagedDocument のメイン コンテキストの子コンテキストですか、それとも親ですか?

ドキュメントは明確ですが (明確な定義については)、残念ながら、UIDocument、UIManagedDocument、およびすべての NSManagedOjectContext に関する完全なドキュメント セットを読む必要があります。

(皮肉なことに、iCloud がどのように機能するかを読んで) 何が起こっているのかを最終的に把握したとき、UIManagedDocument に関連するすべての問題が解消されたように見えました。

基本的に、次の簡単なルールに従います。

  1. UIManagedDocument に埋め込まれたコンテキストで直接「保存」メソッドを呼び出さないでください。

  2. オブジェクトが「ダーティ」で保存する必要がある場合は、元に戻すマネージャーに変更を登録するか、[doc updateChangeCount:UIDocumentChangeDone] を直接呼び出します。

  3. 子コンテキストを使用する場合は、すぐに行ってください。この場合に必要なことは、parentContext プロパティを設定し、あなたの子コンテキストで save: を呼び出すことだけです。それ以外はすべて自動的に行われます。

編集 別の危険信号は、異なるコンテキストを使用してコードを実行していることです。その場合は、正しいスレッドから実行していることを確認する必要があります。NSManagedObjectContext オブジェクトはスレッドセーフではありません。したがって、作成されたスレッド (NSConfinementConcurrencyType の場合) からアクセスするか、他の 2 つの同時実行タイプのいずれかの場合は performBlock を使用してアクセスする必要があります。

複数のコンテキストを使用して継続的に挿入/取得している場合は、いくつかの問題を解決する必要があります。主に、変更が反映され、保存されるようにスケジュールされていることを確認する必要があります。

挿入が完了したら、UIManagedDocument の子コンテキストでのみ保存を呼び出すようにしてください。管理ドキュメントで save または saveToURL を呼び出さないでください。上記の方法で保存してください。これにより、変更が正しく反映されます。

フェッチするときは、フェッチをコンテキストのチェーンのずっと上まで行うかどうかを決定する必要があります。NSFetchRequest には多くのオプションがあります (setShouldRefreshRefetchedObjects など)。これらのオプションは、フェッチが独自のコンテキストのみを使用するか、バッキング ストアや他の多くのものに移動するかを決定します。ほとんどの場合、デフォルトが必要ですが、子コンテキストを使用する場合は、より多くの責任が生じます。

また、子コンテキストがある場合は、適切な同時実行タイプでそれらを作成し、適切なスレッド/キュー内からのみ使用していることを確認する必要があります。そうしないと、未定義の結果が得られます。

編集

あなたの最近の質問編集からのこの声明に応えて:

3) ユーザーが変更を加えたい場合、新しい MOC がドキュメント MOC の子として作成されます。これはメイン キューの MOC です。これは、上記のフェッチを実行するために使用されるコンテキストです。

私はこれをやったことがなく、私にとっては別の危険信号です. メイン キュー MOC - document.managedObjectContext が既に存在します。メインスレッドでドキュメントをいじりたい場合は、それを使用してください。CoreData は依然としてスレッド包含モデルで動作し、実際にはスレッドごとに 1 つの MOC のみが必要であることを思い出してください。新しい API は MOC をキューに関連付ける方法を提供し、これを助けますが、スレッドごとに複数の MOC を使用すると問題が発生する可能性があることは間違いありません。一般的に、私はそれを避けます。

于 2012-04-17T19:38:42.777 に答える