通过objectID获取NSManagedObjects数组返回空数组

我试图执行一个提取检索托pipe对象从上下文使用从一个单独的上下文收集的对象ID数组。 但是,获取返回一个空数组。

从“核心数据编程指南” 链接的“检索特定对象”部分:

如果您的应用程序使用多个上下文,并且您想要testing某个对象是否已从持久性存储中删除,则可以使用self ==%@forms的谓词来创build一个提取请求。 您作为variables传入的对象可以是托pipe对象或托pipe对象ID …“

如果您需要testing多个对象的存在性,那么使用IN运算符比为各个对象执行多个提取更为有效,例如:

NSPredicate * predicate = [NSPredicate predicateWithFormat:@“self IN%@”,arrayOfManagedObjectIDs];

虽然这是谈论对象删除的testing,我推测如果对象不被删除,那么结果数组不是空的,并将包含实际NSManagedObjects。 但是,当我执行这个代码:

- (NSArray *)managedObjectsOfEntityName:(NSString *)entityName fromObjectIDs:(NSArray *)objectIDs managedObjectContext:(NSManagedObjectContext *)managedObjectContext { NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName]; request.predicate = [NSPredicate predicateWithFormat:@"self IN %@", objectIDs]; __autoreleasing NSError *error = nil; NSManagedObjectID *testID = objectIDs[0]; NSManagedObject *obj = [managedObjectContext existingObjectWithID:testID error:&error]; if (!obj) { NSLog(@"Unable to perform fetch. Error: %@", error); } error = nil; NSArray *results = [managedObjectContext executeFetchRequest:request error:&error]; if (!results) { NSLog(@"Unable to perform fetch. Error: %@", error); } return results; } 

results数组是非零和空的,而obj是正确填充的。

我已经添加了显式调用existingObjectWithID:作为一个完整性检查,并返回与预期的对象,没有错误。

以下是有关variables的debugging器输出:

 (lldb) po entityName Foo (lldb) po objectIDs <__NSArrayI 0x1170d4950>( 0xd0000000055c0082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p343>, 0xd000000008e80082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p570>, 0xd000000008840082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p545>, 0xd000000006040082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p385>, 0xd000000007740082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p477>, 0xd000000008280082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p522>, 0xd000000008e40082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p569> ) (lldb) po request <NSFetchRequest: 0x10f338840> (entity: Foo; predicate: (SELF IN { 0xd0000000055c0082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p343>, 0xd000000008e80082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p570>, 0xd000000008840082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p545>, 0xd000000006040082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p385>, 0xd000000007740082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p477>, 0xd000000008280082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p522>, 0xd000000008e40082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p569>}); sortDescriptors: ((null)); type: NSManagedObjectResultType; ) (lldb) po request.predicate SELF IN { 0xd0000000055c0082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p343>, 0xd000000008e80082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p570>, 0xd000000008840082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p545>, 0xd000000006040082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p385>, 0xd000000007740082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p477>, 0xd000000008280082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p522>, 0xd000000008e40082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p569>} (lldb) po testID 0xd0000000055c0082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p343> (lldb) p testID.isTemporaryID (bool) $7 = false (lldb) p obj (NSManagedObject *) $9 = 0x000000010f338630 (lldb) po results <__NSArrayI 0x10f205c70>( ) (lldb) po testID.entity nil 

testID上的nil entity是奇怪的,但我不确定这是“坏”。

所以,我显然不知道为什么取回是空的。 实体名称是正确的,取词和谓词看起来不错,而对象是在上下文中,但仍然是零结果。

附加上下文:

从本质上讲,更大的画面是我有一个使用自己的pipe理对象上下文(MOC)来执行一些工作的后台操作。 这个工作的结果在主队列上是需要的,所以我打包工作产生的对象标识并把它们传递给主队列。 在主队列中,我需要返回实际的pipe理对象,所以我试图从主队列的MOC中通过objectID获取它们。 我意识到我可以在MOC上使用objectWithID:existingObjectWithID:error:甚至objectRegisteredForID:来获得这些对象,但是每个对象都有自己的特殊问题:

  • objectWithID:如果对象不在上下文中,可能返回一个伪造的对象,如果它在上下文中,则返回一个错误。
  • existingObjectWithID:error:很好,因为我们将返回nil (而不是一个假对象),但它也返回一个错误。
  • objectRegisteredForID:如果对象不在上下文中,将返回nil

因此,如果我在一个循环中使用objectWithID:existingObjectWithID:error:来获取一堆对象,那么可能会有n次访问持久存储来错误对象,这意味着性能可能会很糟糕。 如果我使用objectRegisteredForID:如果碰巧不在主队列的MOC中,我可能不会得到对象。

所以,我并没有尝试迭代数组,而是期望获取请求会限制与持久存储库交互的开销,并一次性返回所需的所有对象。

加成:

顺便说一下,这个问题真的感觉就像是与@"self IN %@"谓词有关,因为我可以移除该谓词并获取entityName所有对象而没有问题。 我也尝试了@"objectID IN %@"作为谓词与相同的(零)结果。

好吧,准备好去核心数据兔子洞了吗?

TL; DR

NSManagedObjectID的持久性存储协调器不再存在于内存中,即使它们的URIRepresentation表示法相同,它们URIRepresentation失去其NSEntityDescriptionentity ),并且不等同于( isEqual:返回NONSManagedObjectID

在兔子洞下

甜…我们走吧

请记住,我正在一个单独的线程上从一个单独的托pipe对象上下文(MOC)中收集objectID的数组? 好。 但是,我没有提到,交通部正在使用自己的持久店铺协调员(当然指着同一个档案)。 PSC和MOC是短暂的,因为一旦工作完成,它们被自动释放,但是当它们还活着的时候,我把NSManagedObjectID实例保存到一个NSArray (它被传递给另一个线程)。

一旦收到在单独的线程, NSManagedObjectID有一个nil实体。 这似乎正在发生(这里的教育猜测…),因为这些objectIDs从哪里来的PSC现在不再在内存中, NSManagedObjectID必须保留NSEntityDescription必须持有的NSEntityDescriptionentity )的一周引用。 这个nil entity似乎会引起问题,因为评论者怀疑…

我不能确定的内部,但它会出现,如果没有entityNSManagedObjectID 等于另一个NSManagedObjectID具有相同的URIRepresentation 。 让我说明一下:

在以下代码中:* objectID是一个来自持久存储的NSManagedObjectID * ,它不再在内存中,其实体属性为nil 。 * managedObjectContext是我们当前线程上的MOC(当然)

 NSURL *URIRep = objectID.URIRepresentation; NSManagedObjectID *newObjectID = [managedObjectContext.persistentStoreCoordinator managedObjectIDForURIRepresentation:URIRep]; 

如果我们在debugging器中查看这个…猜猜看是什么:

 (lldb) p [ojectID isEqual:newObjectID] (bool) $0 = false 

尽pipe这两个objectID的URIRepresentation表示必须相同,但这些对象并不等同。

这几乎可以肯定为什么谓词[NSPredicate predicateWithFormat:@"self IN %@", objectIDs]无法匹配任何对象,并导致获取请求返回零对象。

然而有趣的是,在以下代码中,其中testID是来自持久性存储的NSManagedObjectID * ,该存储不再位于内存中,其实体属性nil ,则从MOC中检索对象时不会出现问题:

 NSManagedObject *obj = [managedObjectContext existingObjectWithID:testID error:&error]; 

解决方法

有两件事我已经validation了解决方法:

  1. 在上下文之间传递NSManagedObjectID时,请使用相同的持久性存储协调器(PSC)(并确保它驻留在内存中)。 如果这样做, NSManagedObjectID s永远不会失去他们的entity ,一切都按预期工作。

  2. 而不是从上下文传递NSManagedObjectID ,使用他们的URIRepresentation 。 即不是传递一个NSManagedObjectID对象数组,而是传递一个表示每个NSManagedObjectID URIRepresentationNSURL数组。 一旦准备好使用这些objectID执行一次提取操作,就使用managedObjectIDForURIRepresentation:消息从当前MOC的PSC中获取一个NSManagedObjectID

选项1比较简单,但是在并发时间方面可能会有一些缺点,例如,如果您想为您的操作提供一个单独的PSC(例如只读),则限制太大。

在我调用managedObjectIDForURIRepresentation:经验(相对有限) managedObjectIDForURIRepresentation:它看起来是非常高性能的,所以将URIRepresentation NSURL s转换为NSManagedObjectID s似乎不会增加太多的开销。

谢谢

感谢您遵循…我希望这有助于清楚地解释问题和解决方法。 我当然觉得我通过挖掘所有这些,在核心数据对象ID和持久性商店协调员中获得了优异的成绩。

干杯,

列维