首先,Core Data 是线程安全的。但是你必须遵守规则:
-
NSManagedObjectContext 是线程绑定的。您只能在分配给它的线程上使用它。 -init 导致将上下文分配给创建它的线程。使用 -initWithConcurrencyType: 将允许您创建与其他线程/队列关联的上下文。
- 与
NSManagedObjectContext 关联的任何NSManagedObject 都绑定到与其来自的上下文相同的线程/队列
- 没有第三条规则
您可以在线程之间传递NSManagedObjectID 实例,但必须遵守规则 1 和 2。根据您的描述,我认为您违反了这些规则。
我个人也不推荐使用 NSManagedObjectID。有更好的解决方案。 ——马库斯·S·扎拉
Marcus,这是我读过的关于 Core Data 线程的最简洁的解释。自从它推出以来一直使用它,有几天我仍然把这些规则弄错了!你提到了“更好的解决方案”——你能详细说明一下吗?
我非常不信任NSManagedObjectID 的使用。在许多情况下,它不会从一个应用程序生命周期到另一个应用程序生命周期保持不变。最初,根据文档,我们(一般的 Cocoa 开发人员)认为这是为我们生成的神话主键。事实证明这是不正确的。
在具有父/子上下文的现代开发中,情况更加混乱,我们需要注意一些有趣的陷阱。鉴于目前的情况,我比以前更不喜欢它。那我们用什么?
我们应该自己生成。它不需要太多。如果您的数据还没有来自服务器的主键(很常见的是来自基于 Ruby 的服务器的 id)然后创建一个。我喜欢称它为guid,然后有一个类似于此的-awakeFromInsert:
- (void)awakeFromInsert
{
[super awakeFromInsert];
if (![self primitiveValueForKey:@"guid"]) {
[self setPrimitiveValue:[[NSProcessInfo processInfo] globallyUniqueString] forKey:@"guid"];
}
}
注意:此代码是在网络浏览器中编写的,可能无法编译。
您检查该值是因为 -awakeFromInsert 在每个上下文中调用一次。然后我通常会在我的NSManagedObject 实例上使用一种方便的方法,类似于:
@implementation MyManagedObject
+ (MyManagedObject*)managedObjectForGUID:(NSString*)guid inManagedObjectContext:(NSManagedObjectContext*)context withError:(NSError**)error
{
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:[self entityName]];
[fetch setPredicate:[NSPredicate predicateWithFormat:@"guid == %@", guid]];
NSArray *results = [context executeFetchRequest:request error:error];
if (!results) return nil;
return [results lastObject];
}
@end
注意:此代码是在网络浏览器中编写的,可能无法编译。
这将错误处理和上下文/线程控制留给开发人员,但提供了一种方便的方法来检索当前上下文中的对象,并允许我们将对象从一个上下文“反弹”到另一个上下文。
这比 -objectWithID: 慢,应该小心使用,并且只有在保存后需要将对象从一个上下文反弹到另一个上下文的情况下才能将其移到堆栈上。
和我做的大多数事情一样; 这不是通用解决方案。这是一个应该根据每个项目进行调整的基线。