通过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
失去其NSEntityDescription
( entity
),并且不等同于( isEqual:
返回NO
) NSManagedObjectID
。
在兔子洞下
甜…我们走吧
请记住,我正在一个单独的线程上从一个单独的托pipe对象上下文(MOC)中收集objectID的数组? 好。 但是,我没有提到,交通部正在使用自己的持久店铺协调员(当然指着同一个档案)。 PSC和MOC是短暂的,因为一旦工作完成,它们被自动释放,但是当它们还活着的时候,我把NSManagedObjectID
实例保存到一个NSArray
(它被传递给另一个线程)。
一旦收到在单独的线程, NSManagedObjectID
有一个nil
实体。 这似乎正在发生(这里的教育猜测…),因为这些objectIDs从哪里来的PSC现在不再在内存中, NSManagedObjectID
必须保留NSEntityDescription
必须持有的NSEntityDescription
( entity
)的一周引用。 这个nil
entity
似乎会引起问题,因为评论者怀疑…
我不能确定的内部,但它会出现,如果没有entity
的NSManagedObjectID
不等于另一个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了解决方法:
-
在上下文之间传递
NSManagedObjectID
时,请使用相同的持久性存储协调器(PSC)(并确保它驻留在内存中)。 如果这样做,NSManagedObjectID
s永远不会失去他们的entity
,一切都按预期工作。 -
而不是从上下文传递
NSManagedObjectID
,使用他们的URIRepresentation
。 即不是传递一个NSManagedObjectID
对象数组,而是传递一个表示每个NSManagedObjectID
URIRepresentation
的NSURL
数组。 一旦准备好使用这些objectID执行一次提取操作,就使用managedObjectIDForURIRepresentation:
消息从当前MOC的PSC中获取一个NSManagedObjectID
。
选项1比较简单,但是在并发时间方面可能会有一些缺点,例如,如果您想为您的操作提供一个单独的PSC(例如只读),则限制太大。
在我调用managedObjectIDForURIRepresentation:
经验(相对有限) managedObjectIDForURIRepresentation:
它看起来是非常高性能的,所以将URIRepresentation
NSURL
s转换为NSManagedObjectID
s似乎不会增加太多的开销。
谢谢
感谢您遵循…我希望这有助于清楚地解释问题和解决方法。 我当然觉得我通过挖掘所有这些,在核心数据对象ID和持久性商店协调员中获得了优异的成绩。
干杯,
列维
- 核心数据 – fetchBatchSize被忽略
- “无法加载NSManagedObjectModel。 nil是非法的URL参数“
- 如何在核心数据中移动行 – 使用Faulting教程 – IOS
- 使用核心数据与每个实体的许多图像?
- 魔法logging – 取主线程块ui,在后台返回nil
- NSFetchedResultsController在合并“NSManagedObjectContextDidSaveNotification”后没有显示所有结果
- 如何在Core Data上下文之外使用Core Data模型子类?
- NSPredicate与string完全匹配
- 如何在今天的扩展(iOS)中访问CoreData模型