我已经阅读了大量有关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