我已經閱讀了大量有關NSManagedObjectContext和多線程應用程序的文章。我還瀏覽了CoreDataBooks示例,瞭解獨立線程如何需要自己的NSManagedObjectContext,以及如何將保存操作與主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];
}
}
在我們執行合併,如果commit
稱爲被通知contextDidSave:
功能。
+(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];
}
這編譯並似乎工作,但我知道線程問題可能會非常棘手,由於競爭條件。有人看到這種方法有問題嗎?
此外,我從異步請求(使用ASIHTTPRequest)的上下文中使用它,它從服務器獲取一些數據並更新並在iPhone上插入存儲。看起來NSThreadWillExitNotification在請求完成後不會觸發,並且隨後的請求會使用同一個線程。這意味着相同的NSManagedObjectContext用於同一個線程上的不同請求。這是一個問題嗎?
克里斯,我面臨着一個類似的多線程問題,同時使用在主線程中創建一個NSManagedObjectContext在NSoperation隊列中的所有操作。當每個線程試圖保存上下文時,應用程序崩潰會隨機拋出核心數據的異常。我正在考慮在所有操作中使用這個上下文,以便每個操作都擁有上下文的獨佔訪問權限。我已經閱讀過你的解決方案。你可以粘貼我用於合併上下文的新代碼,也可以使用鎖來播放遊戲 – 2011-05-14 18:36:48