基本的にはNSManagedObjectContextはスレッド毎に作成して利用しなければいけません。以下は、その為にスレッド毎にContextを作成して管理するためのコードです。複雑なアプリでなければ、この管理法で十分でしょう。シングルトンなManagerクラスでコンテキストを管理する方法は簡単ですが、スレッドを多用する場合はスレッド毎に管理する方が良いでしょう。
managedObjectContextForCurrentThreadでコンテキストを取得し、保存は[NSManagedObjectContext save:&error]のように、クラスメソッドを呼びます。保存するとNSNotificationでコンテキスト自身のmanagedObjectContextDidSave:が実行され、メインスレッドのコンテキストに変更が反映されます。スレッド毎に自動的にthreadDictionaryにNSManagedObjectContextを登録し、スレッドが破棄されるとコンテキストも破棄されるようになっているので、明示的にコンテキストを破棄する必要はありません。
@interface NSManagedObjectContext (Extras) + (NSManagedObjectContext *)managedObjectContextForThread:(NSThread *)thread; + (NSManagedObjectContext *)managedObjectContextForCurrentThread; + (NSManagedObjectContext *)managedObjectContextForMainThread; + (BOOL)save:(NSError **)error; @end
#import "NSManagedObjectContextExtras.h"
NSString * const NSManagedObjectContextThreadKey = @"NSManagedObjectContextThreadKey";
@interface NSManagedObjectContext ()
- (void)managedObjectContextDidSave:(NSNotification*)notification;
@end
@implementation NSManagedObjectContext (Extras)
+ (NSManagedObjectContext *)managedObjectContextForThread:(NSThread *)thread {
NSMutableDictionary *threadDictionary = [thread threadDictionary];
NSManagedObjectContext *context = [threadDictionary objectForKey:NSManagedObjectContextThreadKey];
if (!context) {
#ifdef TARGET_OS_IPHONE
id appDelegate = [[UIApplication sharedApplication] delegate];
#else
id appDelegate = [NSApp delegate];
#endif
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
if ([[NSThread currentThread] isMainThread]) {
context = mainContext;
} else {
context = [[[NSManagedObjectContext alloc] init] autorelease];
[context setPersistentStoreCoordinator:[mainContext persistentStoreCoordinator]];
}
[threadDictionary setObject:context forKey:NSManagedObjectContextThreadKey];
}
return context;
}
+ (NSManagedObjectContext *)managedObjectContextForCurrentThread {
return [NSManagedObjectContext managedObjectContextForThread:[NSThread currentThread]];
}
+ (NSManagedObjectContext *)managedObjectContextForMainThread {
return [NSManagedObjectContext managedObjectContextForThread:[NSThread mainThread]];
}
+ (BOOL)save:(NSError **)error {
NSManagedObjectContext *context = [NSManagedObjectContext managedObjectContextForCurrentThread];
BOOL isMainThread = [[NSThread currentThread] isMainThread];
if (!isMainThread) {
[[NSNotificationCenter defaultCenter] addObserver:context
selector:@selector(managedObjectContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:context];
}
BOOL result = [context save:error];
if (!isMainThread) {
[[NSNotificationCenter defaultCenter] removeObserver:context
name:NSManagedObjectContextDidSaveNotification
object:context];
}
return result;
}
- (void)managedObjectContextDidSave:(NSNotification*)notification {
NSManagedObjectContext *context = [NSManagedObjectContext managedObjectContextForMainThread];
[context performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
@end