マルチスレッドのCoreData対応アプリで問題が発生しているので、自分が何をどのように行っているかをしっかりと確認する必要があると考えました。次のことがうまくいくかどうか教えてください。
CoreDataのものを処理するシングルトンDataManager
クラスがあります。managedObjectContext
スレッドごとに異なるMOCを返すプロパティがあります。したがって、NSMutableDictionary *_threadContextDict
(コンテキストへの文字列スレッド名)とNSMutableDictionary *_threadDict
(スレッドへの文字列スレッド名)が与えられると、次のようになります。
-(NSManagedObjectContext *)managedObjectContext
{
if ([NSThread currentThread] == [NSThread mainThread])
{
MyAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
return delegate.managedObjectContext; //MOC created in delegate code on main thread
}
else
{
NSString *thisThread = [[NSThread currentThread] description];
{
if ([_threadContextDict objectForKey:thisThread] != nil)
{
return [_threadContextDict objectForKey:thisThread];
}
else
{
NSManagedObjectContext *context = [[NSManagedObjectContext alloc]init];
MyAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[context setPersistentStoreCoordinator:delegate.persistentStoreCoordinator];
[_threadContextDict setObject:context forKey:thisThread];
[_threadDict setObject:[NSThread currentThread] forKey:thisThread];
//merge changes notifications
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification object:context];
return context;
}
}
}
}
このmergeChanges
方法では、着信通知からの変更を、通知を生成したコンテキストを除くすべてのコンテキストにマージします。次のようになります。
-(void)mergeChanges:(NSNotification *)notification
{
MyAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = delegate.managedObjectContext;
[context performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification)
withObject:notification waitUntilDone:YES];
for (NSString *element in [_threadContextDict allKeys])
{
if (![element isEqualToString:[[NSThread currentThread] description]])
{
NSThread *thread = [_threadDict objectForKey:element];
NSManagedObjectContext *threadContext = [_threadContextDict objectForKey:element];
[threadContext performSelector:@selector(mergeChangesFromContextDidSaveNotification)
onThread:thread withObject:notification waitUntilDone:YES];
}
}
}
MOCに変更を保存するときはいつでも、前述のプロパティから取得したコンテキストを呼び出す、saveContext
この共有のメソッドの呼び出しで行われます。DataManager
save
-(void)saveContext
{
NSManagedObjectContext *context = self.managedObjectContext;
NSError *err = nil;
[context save:&err];
//report error if necessary, etc.
}
Core Dataマルチスレッドルールについての私の理解を考えると、これはうまくいくはずだと思います。スレッドごとに個別のコンテキストを使用していますが、すべてのスレッドに同じ永続ストアがあります。しかし、これを使用すると、スレッドが同じオブジェクト(NSManagedObject
サブクラス)で機能していなくても、多くのマージの競合が発生します。ネットワークからデータをダウンロードし、結果を解析してCoreDataに保存しているだけです。
私は何か間違ったことをしていますか?インスタンスを使用していくつかのことをロックしようとしましNSLock
たが、ハングアップします。
更新/解決策:1つの簡単なことを追加することで、この作業を行うことができました。それは、終了時に辞書からスレッド/MOCペアを削除する方法です。dispatch_async
Core Dataを実行する各呼び出しの各ブロックの最後で、を呼び出します[self removeThread]
。これにより、現在のスレッドとそのMOCがディクショナリから削除されます。また、変更をメインスレッドMOCにマージするだけです。事実上、これは、バックグラウンドスレッドで作業するたびに、新しいMOCを取得することを意味します。
userInfoDict
また、を呼び出す代わりに、に番号を追加することでスレッドを区別しdescription
ます。番号は、呼び出されるたびにより高い番号を返すクラスの読み取り専用プロパティによって取得されます。