ここで、NSManagedObjectContext とマルチスレッド アプリケーションに関する多くの投稿を読みました。また、CoreDataBooks の例を調べて、個別のスレッドが独自の NSManagedObjectContext を必要とする方法と、保存操作がメインの NSManagedObjectContext とマージされる方法を理解しました。この例は良いものですが、アプリケーション固有すぎることもわかりました。私はこれを一般化しようとしていますが、私のアプローチが正しいかどうか疑問に思っています。
私のアプローチは、現在のスレッドの NSManagedObjectContext を取得するための汎用関数を持つことです。この関数はメイン スレッドの NSManagedObjectContext を返しますが、別のスレッド内から呼び出された場合は新しいものを作成します (またはキャッシュからフェッチします)。それは次のようになります。
+(NSManagedObjectContext *)managedObjectContext {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread]) {
return moc;
}
// a key to cache the context for the given thread
NSString *threadKey = [NSString stringWithFormat:@"%p", thread];
// delegate.managedObjectContexts is a mutable dictionary in the app delegate
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;
if ( [managedObjectContexts objectForKey:threadKey] == nil ) {
// create a context for this thread
NSManagedObjectContext *threadContext = [[[NSManagedObjectContext alloc] init] autorelease];
[threadContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]];
// cache the context for this thread
[managedObjectContexts setObject:threadContext forKey:threadKey];
}
return [managedObjectContexts objectForKey:threadKey];
}
メインスレッドから呼び出された場合、保存操作は簡単です。他のスレッドから呼び出された保存操作は、メイン スレッド内でマージする必要があります。そのために、私は一般的なcommit
機能を持っています:
+(void)commit {
// get the moc for this thread
NSManagedObjectContext *moc = [self managedObjectContext];
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread] == NO) {
// only observe notifications other than the main thread
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
NSError *error;
if (![moc save:&error]) {
// fail
}
if ([thread isMainThread] == NO) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
}
の通知によって呼び出された場合、contextDidSave:
関数でマージを実行しcommit
ます。
+(void)contextDidSave:(NSNotification*)saveNotification {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
[moc performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:saveNotification
waitUntilDone:YES];
}
最後に、これを使用して NSManagedObjectContext のキャッシュをクリーンアップします。
+(void)initialize {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(threadExit)
name:NSThreadWillExitNotification
object:nil];
}
+(void)threadExit {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *threadKey = [NSString stringWithFormat:@"%p", [NSThread currentThread]];
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;
[managedObjectContexts removeObjectForKey:threadKey];
}
これはコンパイルされ、機能しているように見えますが、競合状態のためにスレッドの問題が厄介になる可能性があることはわかっています。このアプローチに問題がある人はいますか?
また、サーバーから一部のデータをフェッチし、iPhone のストアを更新して挿入する非同期要求 (ASIHTTPRequest を使用) のコンテキスト内からこれを使用しています。リクエストの完了後に NSThreadWillExitNotification が起動されないようで、その後のリクエストには同じスレッドが使用されます。これは、同じ NSManagedObjectContext が同じスレッドの個別の要求に使用されることを意味します。これは問題ですか?