1

我们有一个后台线程需要进行一些获取..但它不需要任何数据——只需要 objectID

最初我们只是为此使用特定的新创建的空白托管上下文来执行此操作。

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];
...

所以在我的测试中它从未崩溃,在文档中我不明白为什么它也不应该工作......我的意思是我没有得到未保存的东西,我无法得到对象,但是以这种方式使用......

它更快,看起来很优雅,但它是否安全?

4

3 回答 3

3

我整天都在想你的问题。这是我想出的。正如其他人指出的那样,NSPersistentStoreCoordinator对象不是线程安全的。当不同线程上的一堆NSManagedObjectContext对象使用相同的对象时NSPersistentStoreCoordinator,它们通过锁定和解锁NSPersistentStoreCoordinator.

但是,您担心只读取数据和线程安全NSManagedObjectID数据。那样可以么?

好吧,Apple 文档 On Concurrency with Core Data提到了与您正在做的事情类似的事情:

例如,您可以将获取请求配置为仅返回对象 ID,但还包括行数据(并更新行缓存)——如果您只是要将这些对象 ID 从后台线程传递到另一个线程,这将很有用.

好的,但是我们需要锁定协调器吗?

通常不需要对托管对象或托管对象上下文使用锁。但是,如果您使用由多个上下文共享的单个持久存储协调器并希望对其执行操作(例如,如果您想添加一个新存储),或者如果您想在一个上下文中将多个操作聚合为如果是虚拟单个事务,则应锁定持久存储协调器。

这似乎很清楚,如果您从多个线程对持久存储执行操作,则应该锁定它。

但是等等 - 这些只是读取操作,它们不应该是安全的吗?好吧,显然不是:

Core Data 不存在读取“安全”但更改“危险”的情况——每个操作都是“危险的”,因为每个操作都有缓存一致性影响并且可能触发故障。

它是我们需要担心的缓存。这就是您需要锁定的原因 - 一个线程中的读取可能会导致另一个线程中的数据因无意的缓存更改而变得混乱。你的代码从来没有给你带来问题,因为这可能真的很少见。但它的那些边缘情况和百万分之一的错误可以造成最大的破坏......

那么,它安全吗?我的答案:

  • 如果在您阅读时没有其他任何东西在使用您的持久存储协调器,是的,您是安全的。
  • 如果您有其他任何东西使用相同的持久存储协调器,请在获取对象 ID 之前将其锁定。
  • 使用托管对象上下文意味着锁定会自动为您处理,因此它也是一个很好的可能性,但看起来您不需要使用它(我同意不要仅仅为了获得一个几个对象 ID)。
于 2012-12-08T23:55:07.037 回答
1

来自 NSPersistentStoreCoordinator 文档:

请注意,如果多个线程直接与协调器一起工作,则它们需要显式地锁定和解锁它。

我想说,如果您要正确锁定 PSC:

[pst lock];
self.objectIDs = [pst executeRequest:request inContext:nil error:&error];
[pst unlock];

根据我对文档的阅读,这将被认为是“安全的”。话虽如此,由 MOC 内部完成的锁定可能是您描述的两种方法之间最显着的性能差异,如果是这种情况,您可能更愿意只使用空白 MOC,因为当您或某人否则以后遇到的代码。

相关问题: NSPersistentStoreCoordinator 线程安全吗?

于 2012-12-07T16:53:39.320 回答
1

没有充分的理由不为此使用托管对象上下文。托管对象上下文给你带来了很多好处——它处理变更管理、线程等。使用持久存储协调器直接失去了很多这种功能。例如,如果您有尚未持久化到此存储的更改,则直接使用持久存储协调器可能会错过它们。

现在您说这对您有吸引力的原因是您只需要托管对象 ID。您似乎真正想要的是找到托管对象,而不是在它们上触发错误。您可以在获取请求中使用 NSManagedObjectResultType 或 NSManagedObjectIDResultType 来执行此操作。在 NSManagedObjectResultType 的情况下,您只需访问获取的对象上的 objectID,这不会触发错误 - 因此不会“获取数据”。如果已经填充了行缓存等,这可能具有一些性能优势。

说了这么多,为什么不直接使用父子上下文来解决这个问题呢?

于 2012-12-08T21:57:56.900 回答