帮助多线程Core Data应用程序设计

时间:2010-12-17 09:54:06

标签: objective-c multithreading core-data thread-safety

alt text

以上是我的模型的简化。我的应用有NSWindowController个对象,可控制用户帐户实体的两个NSViewController个对象。当用户登录应用程序时,他们可以通过调出相关的视图控制器来修改用户或帐户信息。在后台,我让应用程序在一个单独的线程上定期在应用程序委托中填充用户的日志。

我在后台线程中使用单独的NSManagedObjectContext,在视图控制器中使用应用程序委托的NSManagedObjectContext进行数据输入。我想知道一些事情:

1)这是一个好习惯吗?我应该为每个视图控制器创建一个NSManagedObjectContext,然后在用户完成更改后合并上下文吗?

2)因为 log 实体是在后台线程中创建的,所以它拥有自己的NSManagedObjectContext。但是,每个日志都包含来自用户帐户实体的信息,这些信息是在应用程序代理NSManagedObjectContext中创建的。这就是我提取用户的方式:

- (NSManagedObjectID*) fetchUser:(NSString*) userID {   
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];   
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"user":inManagedObjectContext:self.managedObjectContext];
    /** snip **/
}

后台线程调用此方法如下:

NSManagedObjectID* userObjectID = [self fetchUser:userID];
NSManagedObject* userObject = [self.logsManagedObjectContext objectWithID:userObjectID];

我在fetchUser线程安全的做法是什么?如果其中一个视图正在修改同一个用户,是否需要在获取用户时锁定主要托管对象上下文?从this article我理解(也许是错误的)我可能不得不这样做。到目前为止,我没有遇到任何问题,但我不想留下潜在的边缘情况。

3)当其中一个视图控制器对应用程序委托NSManagedObjectContext进行更改时,它会发布一个按如下方式处理的通知:

- (void)contextDidSave:(NSNotification *)notification {
    SEL selector = @selector(mergeChangesFromContextDidSaveNotification:);
    [self.logManagedObectContext performSelector:selector onThread:backgroundThread withObject:notification waitUntilDone:NO];
}

这是我应该如何处理合并,还是应该合并应用程序委托的NSManagedObjectContext?我发现这样做(在主线程上)锁定了UI。

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:9)

NSManagedObjectContext个对象不是线程安全的。这意味着如果您希望从多个线程访问Core Data,则每个线程都需要一个(并且也在线程上创建)。其中每个都可以使用相同的NSPersistentStoreCoordinator,它将序列化对持久性存储的访问。

这是因为每个NSManagedObjectContext 都知道如何正确锁定NSPersistentStoreCoordinator,以避免发生冲突。遵循这些规则,您应保持线程安全。

正如您已经在做的那样,NSManagedObjectID对象应该用于将Core Data对象从一个MOC传递到另一个MOC(并且从一个线程扩展到另一个线程)。但是,您正在调用fetchUser:,它使用主线程中的MOC,在背景线上。这是不正确的。必须从主线程调用fetchUser:方法调用。当然,没有什么可以阻止您使用后台MOC在后台线程中检索用户。

总之,总是从创建它的线程中调用NSManagedObjectContext

这里的技巧是确保两个MOC都知道其他的保存,因此您必须注册以接收来自每个上下文的通知。然后,您应该从MOC的相应线程执行mergeChangesFromContextDidSaveNotification:。目前,您的背景上下文正在通知主线程的上下文中的更改,但反之亦然。

哦,并且没有必要为每个NSViewController设置单独的上下文。作为UI元素,它们与上下文的交互将发生在同一(主)线程上,因此共享很好。