Core Data 固有の属性で説明されている方法を使用して、同じ属性が 2 回持たないようにしています (属性はここでは ID と呼ばれます)。
これは、シングルスレッド環境では問題なく機能します。私はマルチスレッド環境を使用し、スレッド パラダイムごとに 1 つのコンテキストを使用します。
問題は、2 つのスレッドが同時に同じ属性を持つオブジェクトを作成しようとすると、次の問題が発生することです。
- スレッド A はコンテキスト 1 に ID 1 のオブジェクトを作成しますが、そのようなオブジェクトがないため作成されます。
- スレッド B はコンテキスト 2 に ID 1 のオブジェクトを作成しますが、そのようなオブジェクトがないため作成されます。
- スレッド A は context1->context2 を同期します (以下のコードを使用)
同じ ID を持つ 2 つのレコードがあることに気付きます (1)。
テスト中にこの問題が発生したため、まれですが、時間の経過とともに確実に発生します。
もちろん、これを防ぐための GDC やセマフォなどの複数のオプションがありますが、複雑な解決策を検討する前に、誰かがより良い解決策を持っているかどうか、またはイベントをシリアル化するレベルを推奨できるかどうか疑問に思っていました。
iOS5+ および ARC を使用。
コンテキストの同期に次のコードを使用します。
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
と:
- (void)backgroundContextDidSave:(NSNotification *)notification {
/* Make sure we're on the main thread when updating the main context */
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
withObject:notification
waitUntilDone:NO];
return;
}
/* merge in the changes to the main context */
for (NSManagedObjectContext* context in [self.threadsDictionary allValues]){
[context mergeChangesFromContextDidSaveNotification:notification];
}
}
スレッドセーフなコンテキストを取得するには:
/**
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
*/
- (NSManagedObjectContext *) managedObjectContextForThread {
// Per thread, give one back
NSString* threadName = [NSString stringWithFormat:@"%d",[NSThread currentThread].hash];
NSManagedObjectContext * existingContext = [self.threadsDictionary objectForKey:threadName];
if (existingContext==nil){
existingContext = [[NSManagedObjectContext alloc] init];
[existingContext setPersistentStoreCoordinator: [self persistentStoreCoordinator]];
[self.threadsDictionary setValue:existingContext forKey:threadName];
[existingContext setMergePolicy:NSOverwriteMergePolicy];
}
return existingContext;
}