我正在尝试执行提取操作,以使用从不同上下文中收集的对象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似乎引起了问题,因为评论者怀疑...

    我不确定内部结构,但似乎没有entityNSManagedObjectID就是,而不是等于另一个具有相同NSManagedObjectIDURIRepresentation。让我说明一下:

    在下面的代码中:
    * 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对象数组,而是传递代表每个NSURLURIRepresentationNSManagedObjectID数组。一旦准备好使用这些objectID执行提取,就可以使用managedObjectIDForURIRepresentation:消息从当前MOC的PSC中获取NSManagedObjectID

  • 选项1比较简单,但是在并发时序方面可能有一些缺点,并且如果例如您想为自己的操作使用单独的PSC(例如只读),则可能过于严格。

    以我的经验(相对有限),调用managedObjectIDForURIRepresentation:似乎非常有效,因此将URIRepresentation NSURL转换为NSManagedObjectID似乎并没有增加太多开销。

    谢谢

    感谢您的关注...我希望这有助于清楚地说明问题和解决方法。通过挖掘所有这些,我当然感觉像在核心数据对象ID和持久性存储协调器中获得了优异奖章。

    干杯,

    列维

    10-07 20:23