NSFetchRequest 未捕获具有更改属性的对象

Posted

技术标签:

【中文标题】NSFetchRequest 未捕获具有更改属性的对象【英文标题】:NSFetchRequest not catching objects that have a changed property 【发布时间】:2011-08-19 22:51:25 【问题描述】:

我在使用 SQL 存储的 MacOsX 10.6 上遇到了一个奇怪的 CoreData 问题。我有一个名为 FamilyNSManagedObject 子类,其属性为 name,关系 personList 连接到另一个名为 NSManagedObject 的子类 Person,属性为 firstname 和反向关系 family。一个Person 只能有一个family,而一个family 可以有多个Persons。

假设我有一个 Family 对象 family 指向家庭“Doe”,并有 2 个 Person(John 和 Jane)连接到它,我执行以下请求:

NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:@"Person" inManagedObjectContext:managedObjectContext]];
[request setPredicate:[NSPredicate predicateWithFormat:@"family.name=%@",[family name]]];
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];

我得到一个大小为 2 的数组,其中包含 2 个人:Jane 和 John,姓氏为 Doe。现在,如果我使用它的合成访问器更新家庭,就我而言:

[family setName:@"Wheat"]

我无法使用相同的获取请求获取Person 的列表。结果是[array count]0

如果我将谓词更改为以下行,它会再次起作用:

[request setPredicate:[NSPredicate predicateWithFormat:@"family=%@",family]];

所以就好像谓词没有使用族属性名称的更新版本,即使我将NSFetchRequest 设置为默认值(所以includesPendingChanges 返回YES)。这对我来说毫无意义。似乎NSFetchRequest 找到了family 对象,但看不到它的值family.name 已更新,未保存,并且在内存中的managedObjectContext 中。当然,如果我保存了商店,它就会再次运行。

有什么想法吗?我浏览了 Mac 文档,不明白为什么会失败。

【问题讨论】:

【参考方案1】:

我认为这里的关键是理解获取请求。它从持久存储中检索数据,因此很明显,如果您没有保存到持久存储,它将找不到该数据。如果你考虑到这一点,你描述的情况是完全合乎逻辑的。

来自核心数据编程指南:

您无法使用基于瞬态属性的谓词进行获取 (尽管您可以使用瞬态属性在内存中进行过滤 你自己)。此外,获取和 商店类型——有关详细信息,请参阅“商店类型和行为”。到 不过,总结一下,如果您直接执行 fetch,您应该 通常不会添加基于 Objective-C 的谓词或排序描述符 获取请求。相反,您应该将这些应用于 获取。如果您使用数组控制器,则可能需要子类化 NSArrayController 所以你可以让它不将排序描述符传递给 持久存储,而是在数据完成后进行排序 已取走。

【讨论】:

我的属性不是临时属性,它是标准属性,由'property (nonatomic, retain) NSString *name'定义;和“动态名称”。我没有检查数据模型编辑器上的“瞬态”单选。

我阅读了 Apple 文档,它明确指出它可以获取已修改但未更改的对象,这就是为什么 includePendingChanges 默认设置为 TRUE 的原因。我阅读了您引用的文档(Fetching Managed Objects),但它是针对瞬态属性的,而不是针对标准属性的。在同一个文档中,上面有一个使用 NSPredicate 的示例。有意义吗?

我明白你的意思。我不认为这段话是指瞬态属性,它们只是在开头提到。 NSFetchRequest 类参考确实对此非常清楚,这一定是令人困惑的。 -- 但是,你能检查一下NSLog 你从[family name] 得到什么吗?也许您没有检查 @"Wheat"... 如果我要求 [姓氏],我会得到“小麦”。这就是令人费解的事情。对象族在程序中是最新的,实际上是 NSFetchRequest 看不到它。我认为您可以在 NSPredicate 中为 NSFetchRequest 执行“family.name=%@”。 Apple Dev 网站上到处都有它的描述。正如我之前所说,如果我保存数据库,那么它会立即工作。但这似乎与苹果在故障等方面做得很聪明的想法相矛盾......我很困惑。有趣的是,如果你没有“。” (所以参考关系),它有效...... 我的意思是:[family setName="Wheat"];然后对 Family 执行一个新的 NSFetchRequest,其中谓词的形式为:“name=%@”,它可以工作。但是 Person 的“family.name”形式的谓词失败了...... 另一个说明。在 Apple 上,它说:“另一方面,SQL 存储将谓词和排序描述符编译为 SQL,并在数据库本身中评估结果。这主要是为了提高性能,但这意味着评估发生在非Cocoa 环境,因此依赖 Cocoa 的排序描述符(或谓词)无法工作。”它的真正含义是什么?它只获取保存的 sql DB 中的项目?但是,为什么它适用于 Family 类(使用简单的 'name=%@')而不适用于 Person 类(使用 'family.name=%@')?【参考方案2】:

总而言之,我一直在彻底测试我的代码,以下是我如何看待 CoreData 在 Fetching 和 Objective-c 谓词(即点符号)方面的局限性。

如果一个对象已被 Objective-C 程序访问,并且其属性或关系之一已被修改,则任何带有使用点表示法的谓词的 NSFetchRequest 都将返回 SQL 存储的结构,因此结果将是错误的。

对于琐碎的 FamilyPerson 示例,如果您有一个指向 Family 的链接并更改其 名称,对 Person NSEntity 进行的任何查询都不能包含具有以下查询项的谓词

@"family.name=%@"

它确实会使用 SQL 存储中的姓氏进行查询。但是,在进行此类更改后,以下查询将起作用:

@"family=%@"

确实,NSFetchRequest 还是会检索 store 中的信息,但是由于结构没有改变,它会替换 Memory 中检索到的对象,所以后续测试 [姓氏] 将返回更新后的姓名。

小心,您可以使用嵌套谓词,例如:

@"person.family.name=%@"

只要你能保证所有具有person属性的对象,其family没有改变,name也没有改变.如果不是这样,那么您最多可以调用

@"person.family=%@"

或者,如果您不能保证所有 Family 对象都未受影响,则只能

@"person=%@"

当然,另一种方法是在每次进行任何更改时系统地保存: NSManagedObjects 到持久存储,因此所有属性都会更新,然后上述所有符号都会起作用。但是,有时您确实希望阻止节省并强制客户仅在他愿意时才更改其文档(想想 Word、Excel、图片工具等)。希望对您有所帮助。

【讨论】:

【参考方案3】:

如果“使用相同的获取请求”,您的意思是使用您第一次构造的相同获取请求的实例,那么这并不奇怪。您应用的谓词是“family.name = Doe”。一旦家族的名字是“Wheat”,获取请求的谓词就不再匹配它,因为“Wheat”!=“Doe”。

要在更改名称后检索家族,您需要使用与新家族名称匹配的谓词创建NSFetchRequest 的新实例。

如果“使用相同的提取请求”,您的意思是使用使用相同代码构造的不同提取请求,那么我会考虑@Mundi 的答案。

【讨论】:

我确实不是指“相同”的 fetchrequest 对象,而是具有相同谓词外观的新对象。我确实会回复@Mundi。

以上是关于NSFetchRequest 未捕获具有更改属性的对象的主要内容,如果未能解决你的问题,请参考以下文章

iPhone 核心数据:具有不同属性和字母部分的 NSFetchRequest

NSFetchRequest 获取具有下一个最接近字符串属性的对象

NSFetchRequest 具有一对一关系属性 == YES 谓词错误地获取每个实体

NSFetchRequest / 谓词问题

无法读取未定义的属性“样式”——未捕获的类型错误

未捕获的类型错误:无法读取未定义错误的属性“顶部”