NSFetchRequest 未捕获具有更改属性的对象
Posted
技术标签:
【中文标题】NSFetchRequest 未捕获具有更改属性的对象【英文标题】:NSFetchRequest not catching objects that have a changed property 【发布时间】:2011-08-19 22:51:25 【问题描述】:我在使用 SQL 存储的 MacOsX 10.6 上遇到了一个奇怪的 CoreData 问题。我有一个名为 Family
的 NSManagedObject
子类,其属性为 name
,关系 personList
连接到另一个名为 NSManagedObject
的子类 Person
,属性为 firstname
和反向关系 family
。一个Person
只能有一个family
,而一个family
可以有多个Person
s。
假设我有一个 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 存储的结构,因此结果将是错误的。
对于琐碎的 Family 和 Person 示例,如果您有一个指向 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 获取具有下一个最接近字符串属性的对象