我正在尝试执行提取操作,以使用从不同上下文中收集的对象ID数组从上下文中检索托管对象。但是,获取返回的是一个空数组。
在“核心数据编程指南”的“检索特定对象”部分中
link:
在谈论测试对象删除时,我假设如果不删除对象,则结果数组不为空,并且将包含实际的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
数组为非null且为空,而obj
已正确填充。我已经将对
existingObjectWithID:
的显式调用添加为完整性检查,它随预期对象一起返回,没有错误。这是相关变量的调试器输出:
(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
nil
上的entity
testID
很奇怪,但是我不确定这是“不好的”。因此,我显然对提取为何变空感到困惑。实体名称正确,提取和谓词看起来不错,并且对象位于上下文中,但结果仍为零。
其他上下文:
从本质上讲,较大的 View 是我有一个后台操作,该操作使用其自己的托管对象上下文(MOC)来执行某些工作。在主队列上需要该工作的结果,因此我打包了工作产生的objectID,并将其传递给主队列。在主队列上,我需要返回实际的托管对象,因此我试图通过objectID从主队列的MOC中获取它们。我意识到我可以在MOC上使用
objectWithID:
或existingObjectWithID:error:
甚至objectRegisteredForID:
来获取这些对象,但是每个对象都有自己的特殊问题:objectWithID:
可能会返回一个伪造的对象,如果该对象不在上下文中,并且在上下文中它将返回错误。 existingObjectWithID:error:
很棒,因为我们会取回nil
(而不是伪造的对象),但它也会返回错误。 objectRegisteredForID:
将返回nil
。 因此,如果我在循环中使用
objectWithID:
或existingObjectWithID:error:
来获取一堆对象,则可能是n
跳到持久性存储中以对对象进行故障处理,这意味着性能可能会很糟糕。如果我使用objectRegisteredForID:
,即使它恰好不在主队列的MOC中,也可能根本无法获得该对象。因此,我希望不要尝试遍历数组,而是希望获取请求会限制与持久性存储进行交互的开销,并且一次就可以返回我需要的所有对象。
添加:
顺便说一句,这个问题真的感觉与
@"self IN %@"
谓词有关,因为我可以删除该谓词并获取entityName
的所有对象而不会出现问题。我也尝试使用@"objectID IN %@"
作为具有相同(零)结果的谓词。 最佳答案
好吧,准备好沿着Core Data兔子洞旅行了吗?
TL; DR
持久性存储协调器不再位于内存中的NSManagedObjectID
丢失了其NSEntityDescription
(entity
),并且不等于(isEqual:
返回NO
)来自其他持久性存储协调器的NSManagedObjectID
s,即使它们的URIRepresentation
相同。
在兔子洞下
亲爱的...我们走了。
还记得吗,我是在单独的线程上从单独的受管对象上下文(MOC)收集objectID数组吗?好的。但是,我没有提到MOC正在使用其自己的持久性存储协调器(PSC)(当然,指向同一文件)。 PSC和MOC的生存期很短,因为一旦完成工作,它们就会自动发布,但是当它们还存在时,我将NSManagedObjectID
的实例保存到了NSArray
(将其传递给另一个线程)中。
一旦在单独的线程中接收到,NSManagedObjectID
便具有nil
实体。这似乎正在发生(在这里是有根据的猜测...),因为这些objectID所来自的PSC现在不再在内存中,并且NSManagedObjectID
必须保持对PSC必须保留的NSEntityDescription
(entity
)的引用。 nil
entity
似乎引起了问题,因为评论者怀疑...
我不确定内部结构,但似乎没有entity
,NSManagedObjectID
就是,而不是等于另一个具有相同NSManagedObjectID
的URIRepresentation
。让我说明一下:
在下面的代码中:
* objectID
是持久存储中的NSManagedObjectID *
,不再存储在内存中,并且其实体属性为nil
。
* managedObjectContext
是我们当前线程上的MOC(当然)
NSURL *URIRep = objectID.URIRepresentation;
NSManagedObjectID *newObjectID = [managedObjectContext.persistentStoreCoordinator managedObjectIDForURIRepresentation:URIRep];
如果我们在调试器中查看此信息,请猜测:
(lldb) p [ojectID isEqual:newObjectID]
(bool) $0 = false
即使这两个objectID的
URIRepresentation
必须相同,这些对象也不相等。这几乎可以肯定地解释了谓词
[NSPredicate predicateWithFormat:@"self IN %@", objectIDs]
无法匹配任何对象并导致获取请求返回零个对象的原因。但是,有趣的是,在以下代码中,
testID
是来自持久性存储的NSManagedObjectID *
,该持久性存储不再位于内存中,并且其实体属性为nil
,可以从MOC检索对象而不会出现问题:NSManagedObject *obj = [managedObjectContext existingObjectWithID:testID error:&error];
解决方法
我已经验证了两件事,可以解决此问题:
NSManagedObjectID
时,请使用相同的持久性存储协调器(PSC)(并确保其驻留在内存中)。如果这样做,NSManagedObjectID
永远不会丢失其entity
,一切都会按预期进行。 NSManagedObjectID
,而是使用其URIRepresentation
。即,不传递NSManagedObjectID
对象数组,而是传递代表每个NSURL
的URIRepresentation
的NSManagedObjectID
数组。一旦准备好使用这些objectID执行提取,就可以使用managedObjectIDForURIRepresentation:
消息从当前MOC的PSC中获取NSManagedObjectID
。 选项1比较简单,但是在并发时序方面可能有一些缺点,并且如果例如您想为自己的操作使用单独的PSC(例如只读),则可能过于严格。
以我的经验(相对有限),调用
managedObjectIDForURIRepresentation:
似乎非常有效,因此将URIRepresentation
NSURL
转换为NSManagedObjectID
似乎并没有增加太多开销。谢谢
感谢您的关注...我希望这有助于清楚地说明问题和解决方法。通过挖掘所有这些,我当然感觉像在核心数据对象ID和持久性存储协调器中获得了优异奖章。
干杯,
列维