メインスレッドのコアデータオブジェクトを変更してから、バックグラウンドスレッドに保存したいと思います。バックグラウンドスレッドの保存には、メインスレッドの変更が含まれますか?
4 に答える
Core Data をマルチスレッド方式で使用できますが、Apple が推奨するアプローチのいずれかに従う必要があります。
Core Data を使用した並行プログラミングに推奨されるパターンは、スレッドの制限です。各スレッドは、独自の完全にプライベートなマネージド オブジェクト コンテキストを持つ必要があります。
パターンを採用するには、次の 2 つの方法があります。
スレッドごとに個別の管理対象オブジェクト コンテキストを作成し、単一の永続ストア コーディネーターを共有します。これは、一般的に推奨されるアプローチです。
スレッドごとに個別の管理対象オブジェクト コンテキストと永続ストア コーディネーターを作成します。このアプローチでは、複雑さが増し (特に、異なるコンテキスト間で変更を伝達する必要がある場合)、メモリ使用量が増加しますが、並行性が向上します。
特に:
スレッド制限を使用する場合、管理対象オブジェクトまたは管理対象オブジェクト コンテキストをスレッド間で受け渡さないでください。スレッド境界を越えてあるコンテキストから別のコンテキストにある管理対象オブジェクトを「渡す」には、次のいずれかを行います。
そのオブジェクト ID (objectID) を渡し、受信する管理対象オブジェクト コンテキストで objectWithID: または existingObjectWithID:error: を使用します。対応する管理オブジェクトが保存されている必要があります。新しく挿入された管理オブジェクトの ID を別のコンテキストに渡すことはできません。
受信コンテキストでフェッチを実行します。これらは、受信コンテキストで管理対象オブジェクトのローカル バージョンを作成します。
このことから、(独自のコンテキストを持つ) スレッドで管理対象オブジェクトを作成し、それを別のスレッドに保存することはできません。
したがって、目的を達成するためには、スレッド間で管理対象オブジェクト コンテキストまたは永続ストア コーディネーターを共有する必要があります。この場合、ロック技術を適切に使用して、ストアが一貫性のない状態にならないようにする必要があります。
スレッド間で管理対象オブジェクト コンテキストまたは永続ストア コーディネーターを共有する場合は、すべてのメソッド呼び出しがスレッド セーフ スコープから行われるようにする必要があります。ロックの場合、独自のミューテックスを実装する代わりに、管理対象オブジェクト コンテキストと永続ストア コーディネーターで NSLocking メソッドを使用する必要があります。これらのメソッドは、アプリケーションの意図に関するコンテキスト情報をフレームワークに提供するのに役立ちます。つまり、mutex を提供するだけでなく、操作のクラスターのスコープを設定するのにも役立ちます。
コアデータオブジェクトのロックと同期を行う必要がないようにアプリのデザインを変更する機会がある場合は、この方法をお勧めしません。
あなたが求めているものは、ネストされたコンテキストにぴったり合います。プライベートキューコンテキストを作成し、それをpersistent-store-coordinatorに直接接続します。簡単です。現在のコードを取得し、これの代わりに...
managedObjectContext = [[NSManagedObjectContext alloc] init];
これに置き換えてください...
workerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
これで、従来の閉じ込めMOCを、独自の同時実行キューで実行される新しいMOCに置き換えました。
メインスレッドから使用できるコンテキストを取得するには、別の管理対象オブジェクトコンテキストを作成し、作成したコンテキストの子にします...
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
managedObjectContext.parent = workerManagedObjectContext;
これは、以前と同じようにmanagedObjectContextを使用できることを意味します。ただし、現在は、ストアに直接送信されるのではなく、中間コンテキストを通過します。親コンテキストは、独自のバックグラウンドスレッドで作業を実行します。
したがって、必要なすべての変更をmanagedObjectContextに加えることができます。保存するときは、次のようにします...
static void saveManagedObjectContext(NSManagedObjectContext *moc, void(^completionBlock)(NSError *error)) {
[moc performBlock:^{
NSError *error = nil;
if (moc.hasChanges && [moc save:&error] && moc.parentContext) {
saveManagedObjectContext(moc.parentContext, completionBlock);
} else {
completionBlock(error);
}
}];
}
編集
これを普遍的に使用したい場合は、NSManagedObjectContextのカテゴリに簡単に追加して、...を呼び出すだけです。
[managedObjectContext saveWithCompletionBlock:^(NSError *error){
if (error) {
// Handle the error
return;
}
// Handle success...
}];
Appleは、個別のスレッドに個別のコンテキストを使用することをお勧めします。保存を呼び出すと、実際のデータベースに保存されるのはコンテキスト状態です。メインスレッドの変更をバックグラウンドスレッドに反映させる必要がある場合は、バックグラウンドスレッドコンテキストをメインスレッドコンテキストとマージし、saveを呼び出します。