我们有一个后台线程需要做一些提取..但它不需要任何数据 - 只有objectIDs
最初我们使用特定的新创建的空白托管上下文来完成此操作。
NSFetchRequest *request = [DKDocumentDetails requestAllWithPredicate:predicate inContext:ctx];
[request setResultType:NSManagedObjectIDResultType];
self.objectIDs = [DKDocumentDetails executeFetchRequest:request inContext:ctx];
...
但最近我发现,我也可以在PST上做这个,没有任何上下文我不想要托管对象,但只有ID
NSFetchRequest *request = [DKDocumentDetails requestAllWithPredicate:predicate inContext:mainctx /*used in the wrong thread but only for getting entity description*/];
[request setResultType:NSManagedObjectIDResultType];
NSError *error = nil;
self.objectIDs = [pst executeRequest:request inContext:nil error:&error];
...
所以在我的测试中它永远不会崩溃,在文档中我不明白为什么它也不应该工作......我的意思是我没有得到未保存的东西,我不能得到对象,但是这样使用...
速度更快,看起来更优雅,但是否安全?
答案 0 :(得分:3)
我一整天都在考虑你的问题。这就是我想出来的。正如其他人指出的那样,NSPersistentStoreCoordinator
对象不是线程安全的。当各个线程上的一堆NSManagedObjectContext
个对象使用相同的NSPersistentStoreCoordinator
时,他们会通过锁定和解锁NSPersistentStoreCoordinator
来实现此目的。
但是,您担心的是只读取数据,并在此处使用线程安全的NSManagedObjectID
数据。那可以吗?
嗯,Apple documentation On Concurrency with Core Data提到了类似于你正在做的事情:
例如,您可以将获取请求配置为仅返回对象ID,但也包括行数据(并更新行缓存) - 如果您要从后台线程传递这些对象ID,这可能很有用到另一个线程。
好的,但是我们需要锁定协调员吗?
通常不需要使用具有托管对象或托管对象上下文的锁。但是,如果您使用由多个上下文共享的单个持久性存储协调器并希望对其执行操作(例如,如果要添加新存储),或者您希望将一个上下文中的多个操作聚合为一起如果是虚拟单个事务,则应锁定持久性存储协调器。
这似乎很清楚,如果你在多个线程的持久存储上执行操作,你应该锁定它。
但等等 - 这些只是读操作,它们不应该是安全的吗?好吧,显然不是:
核心数据不会出现读取“安全”但变化“危险”的情况 - 每次操作都是“危险的”,因为每个操作都有缓存一致性影响并且可能触发故障。
我们需要担心的缓存。这就是你需要锁定的原因 - 一个线程中的读取可能导致另一个线程中的数据因无意的缓存更改而变得混乱。您的代码从未给您带来任何问题,因为这可能非常罕见。但它的边缘情况和1万分之一的漏洞可以造成最大的破坏......
那么,这样安全吗?我的回答:
答案 1 :(得分:1)
来自NSPersistentStoreCoordinator docs:
请注意,如果多个线程直接与协调器一起工作,则需要明确锁定和解锁它。
我会说,如果你要正确锁定PSC:
[pst lock];
self.objectIDs = [pst executeRequest:request inContext:nil error:&error];
[pst unlock];
根据我对文档的阅读,这将被视为“安全”。话虽如此,MOC内部锁定可能是您描述的两种方法之间最显着的性能差异,如果是这种情况,您可能更愿意使用空白MOC,因为当您或某人时,这不会令人惊讶否则会遇到代码。
答案 2 :(得分:1)
没有充分的理由不为此使用托管对象上下文。托管对象上下文为您带来了很多 - 它处理变更管理,线程等。使用持久性存储协调器直接失去了很多这样的功能。例如,如果您还没有持久保存到此商店的更改,则可能会直接使用持久性存储协调器来错过它们。
现在你说这对你有吸引力的原因是你只需要托管对象ID。你真正想要的是找到托管对象但不会发现它们的故障。您可以在获取请求中使用NSManagedObjectResultType或NSManagedObjectIDResultType执行此操作。在NSManagedObjectResultType的情况下,您只需访问获取的对象上的objectID,它不会触发故障 - 因此不会“获取数据”。如果已经填充了行缓存等,这可以带来一些性能优势。
所有这些都说明了,为什么不使用父子语境来解决这个问题呢?