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

时间:2014-07-25 17:15:31

标签: ios objective-c core-data

我尝试使用从单独的上下文收集的对象ID数组来执行提取以从上下文中检索托管对象。但是,fetch会返回一个空数组。

从"检索特定对象" "核心数据编程指南" link

  

如果您的应用程序使用多个上下文并且您想测试是否已从持久性存储中删除对象,则可以使用self ==%@形式的谓词创建一个获取请求。作为变量传入的对象可以是托管对象,也可以是托管对象ID ..."

     

如果需要测试是否存在多个对象,则使用IN运算符比对单个对象执行多次提取更有效,例如:

     

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

虽然这是关于测试对象删除,但我假设如果没有删除对象,那么结果数组不为空,并且将包含实际的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:作为完整性检查,然后返回预期的对象,没有错误。

这里是相关变量的调试器输出:

(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很奇怪,但我不确定这是不是很糟糕。&#34;

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

其他背景:

基本上,更大的图片是我有一个后台操作,它使用自己的托管对象上下文(MOC)来执行一些工作。主要队列需要完成该工作的结果,因此我将工作产生的objectID打包并将其传递给主队列。在主队列中,我需要返回实际的托管对象,所以我试图通过objectID从主队列的MOC中获取它们。我知道我可以在MOC上使用objectWithID:existingObjectWithID:error:甚至objectRegisteredForID:来获取这些对象,但每个对象都有自己的特殊问题:

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

因此,如果我在循环中使用objectWithID:existingObjectWithID:error:来获取一堆对象,则可能会n跳转到持久存储以使对象出错,这意味着性能可能会很糟糕。如果我使用objectRegisteredForID:我可能根本没有得到该对象,如果它恰好不在主队列的MOC中。

因此,我没有尝试迭代数组,而是期望获取请求会限制与persistance存储交互的开销,并一举返回我需要的所有对象。

增加:

顺便说一下,问题确实与@"self IN %@"谓词有关,因为我可以删除该谓词并获取entityName的所有对象而不会出现问题。我也尝试@"objectID IN %@"作为具有相同(零)结果的谓词。

1 个答案:

答案 0 :(得分:12)

好的,准备好沿着Core Data兔子洞旅行了吗?

<强> TL; DR

NSManagedObjectID其持久性存储协调员不再在内存中丢失NSEntityDescriptionentity)并且不等于(isEqual:返回NONSManagedObjectID来自不同的持久性商店协调员,即使他们的URIRepresentation是相同的。

打下兔子洞

甜蜜......我们走了。

请记住,我是在单独的线程上从单独的托管对象上下文(MOC)收集objectID数组?好。但是,我没有提到MOC正在使用自己的持久存储协调器(PSC)(当然指向同一个文件)。 PSC和MOC是短暂的,因为一旦工作完成它们就会被自动释放,但是当它们还活着时,我将NSManagedObjectID的实例保存到NSArray(它被传递到另一个线程)。

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

我无法确定内部情况,但似乎没有entity entity 等于另一个NSManagedObjectID使用相同的NSManagedObjectID。让我来说明一下:

在以下代码中: * URIRepresentation是来自持久性商店的objectID,它不再存储在内存中,其实体属性为NSManagedObjectID *。 * nil是我们当前主题的MOC(当然)

managedObjectContext

如果我们在调试器中查看这个...猜猜:

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

即使这两个objectID (lldb) p [ojectID isEqual:newObjectID] (bool) $0 = false 必须相同,对象也不等同。

这几乎可以肯定,谓词URIRepresentation无法匹配任何对象并导致获取请求返回零对象。

然而,有趣的是,在以下代码中,[NSPredicate predicateWithFormat:@"self IN %@", objectIDs]是来自持久性存储的testID,其不再在内存中且其实体属性为NSManagedObjectID *,则检索对象来自MOC没有问题:

nil

解决方法

我已经验证了两件事情的解决方法:

  1. 在上下文之间传递NSManagedObject *obj = [managedObjectContext existingObjectWithID:testID error:&error]; 时使用相同的持久存储协调器(PSC)(并确保它保持驻留在内存中)。如果这样做,NSManagedObjectID永远不会丢失NSManagedObjectID,一切都按预期工作。

  2. 不要将entity从上下文传递到上下文,而是使用他们的NSManagedObjectID。即,不是传递URIRepresentation个对象的数组,而是传递代表每个NSManagedObjectID的{​​{1}}的{​​{1}}个数组。准备好使用这些objectID执行提取后,使用NSURL消息从当前MOC的PSC获取URIRepresentation

  3. 选项1更简单,但可能在并发时序方面存在一些缺点,例如,如果您希望为您的操作设置单独的PSC(例如,只读),则可能限制性太强。

    在我(相对有限的)体验中调用NSManagedObjectID,它似乎非常高效,因此将managedObjectIDForURIRepresentation: NSManagedObjectID转换为managedObjectIDForURIRepresentation: s似乎并不适合加上那么多开销。

    <强>感谢

    感谢您的关注...我希望这有助于清楚地解释问题和解决方法。我当然觉得我通过挖掘所有这些来获得Core Data对象ID和持久性商店协调员的优点徽章。

    干杯,

    列维