首先,Core Data 是线程安全的。但您必须遵守以下规则:
-
NSManagedObjectContext
是线程绑定的。您只能在分配给它的线程上使用它。-init
导致将上下文分配给创建它的线程。使用-initWithConcurrencyType:
将允许您创建与其他线程/队列关联的上下文。
- Any
NSManagedObject
与一个相关联NSManagedObjectContext
与它来自的上下文绑定到相同的线程/队列
- 没有第三条规则
你可以通过NSManagedObjectID
线程之间的实例,但必须遵守规则 1 和 2。根据您的描述,我认为您违反了这些规则。
就我个人而言,我也不建议使用 NSManagedObjectID。有更好的解决方案。 ——马库斯·S·扎拉
Marcus,这是我读过的对 Core Data 线程的最简洁的解释。自从它推出以来,我一直在使用它,有几天我仍然弄错了这些规则!您提到“更好的解决方案”——您能详细说明一下吗?
我对使用NSManagedObjectID
。在许多情况下,从一个应用程序生命周期到另一个应用程序生命周期,它并不保持不变。最初,根据文档,我们(一般 Cocoa 开发人员)认为这是为我们生成的神话主键。事实证明这是不正确的。
在具有父母/孩子背景的现代发展中,情况更加令人困惑,并且有一些有趣的陷阱需要我们警惕。考虑到目前的情况,我比以前更不喜欢它。那么我们用什么呢?
我们应该自己生成。不需要太多。如果您的数据还没有来自服务器的主键(很常见的是有一个id
从基于 Ruby 的服务器)然后创建一个。我喜欢这样称呼它guid
然后有一个-awakeFromInsert
与此类似:
- (void)awakeFromInsert
{
[super awakeFromInsert];
if (![self primitiveValueForKey:@"guid"]) {
[self setPrimitiveValue:[[NSProcessInfo processInfo] globallyUniqueString] forKey:@"guid"];
}
}
NOTE:此代码是在网络浏览器中编写的,可能无法编译。
您检查该值是因为-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
NOTE:此代码是在网络浏览器中编写的,可能无法编译。
这将错误处理和上下文/线程控制留给了开发人员,但提供了一种方便的方法来检索当前上下文中的对象,并让我们将对象从一个上下文“弹跳”到另一个上下文。
这比-objectWithID:
应谨慎使用,并且仅在保存将对象向上移动到堆栈后需要将对象从一个上下文弹回到另一个上下文的情况下使用。
就像我做的大多数事情一样;这不是通用解决方案。这是一个应该根据每个项目进行调整的基线。