gpt4 book ai didi

ios - 通过objectID获取NSManagedObjects数组将返回空数组

转载 作者:可可西里 更新时间:2023-11-01 03:39:14 28 4
gpt4 key购买 nike

我正在尝试执行提取操作,以使用从不同上下文中收集的对象ID数组从上下文中检索托管对象。但是,获取返回的是一个空数组。

在“核心数据编程指南”的“检索特定对象”部分中
link:

If your application uses multiple contexts and you want to test whether an object has been deleted from a persistent store, you can create a fetch request with a predicate of the form self == %@. The object you pass in as the variable can be either a managed object or a managed object ID..."

If you need to test for the existence of several objects, it is more efficient to use the IN operator than it is to execute multiple fetches for individual objects, for example:

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数组为非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和持久性存储协调器中获得了优异奖章。

    干杯,

    列维

    关于ios - 通过objectID获取NSManagedObjects数组将返回空数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24961088/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com