【问题标题】:Crash using Aggregate Operation: "ALL" in a Core Data iOS Application使用聚合操作崩溃:Core Data iOS 应用程序中的“ALL”
【发布时间】:2011-08-20 19:08:37
【问题描述】:

我正在开发一个 iphone 应用程序,并且我使用 Group 和 Contact 对象建立了一个简单的多对多关系。一个组可以有多个联系人,联系人可以属于多个组。

我正在尝试使用以下谓词选择特定联系人尚不属于的所有组。 (注:uid字段是一个字符串字段,我用来唯一标识联系人实体)

[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId]

根据 Apple 的谓词编程指南,ALL 聚合操作是有效的,但我收到以下异常,表明这是一个不受支持的谓词:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported predicate (null)'

我可以使用类似的谓词来选择联系人已经属于使用此谓词的所有组,因此看起来我已正确定义了所有关系和字段。

[NSPredicate predicateWithFormat:@"ANY contacts.uid == %@", contactUId]

构造谓词时引发异常,而不是在我尝试实际执行获取请求时引发异常,因此它似乎与我正在使用的语法有关,而不是与 Core Data 支持有关。我做错了什么?

【问题讨论】:

  • 安布罗斯,欢迎来到 SO。您能否在谓词的定义周围包含更多代码?
  • 我不确定还包括什么。异常在 predicateWithFormat: 调用中引发(而不是在执行 fetch 期间),因此很明显问题与谓词有关,而不是 fetch。
  • 我在一个测试/示例应用程序中重新创建了该场景,并且在对 [NSManagedObjectContext executeFetchRequest:error:] 方法调用的调用中引发了异常,而不是在我最初的 [NSPredicate predicateWithFormat:] 调用中指示。
  • 大家找到解决方案了吗?我遇到了同样的问题:(ALL records.checked == 1) records is a too many relationship on the entity I'm query and check is an NSNumber

标签: iphone ios core-data nspredicate predicate


【解决方案1】:

基本语法没问题。这在我的测试中编译并运行:

NSString *contactUId=@"steve";
NSPredicate *p=[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId];
NSLog(@"p = %@",p);

//=> p = ALL contacts.uid != "steve"

问题很可能出在contactUid 变量上。如果它没有值或没有干净地转换为 NSPredicate 理解的字符串,那么它将导致崩溃。例如。

NSString *contactUId;
NSPredicate *p=[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId];
NSLog(@"p = %@",p);

... 导致您描述的崩溃。

我会在将contactUid 分配给谓词之前立即记录它,以查看其实际的字符串可转换值是什么。它在 NSLog 中的显示方式就是它在谓词中的显示方式,因为两者都使用相同的字符串格式。

【讨论】:

  • 我确实验证了变量的值是正确的。我什至尝试使用硬编码字符串作为参数而不是变量。此外,只需将谓词格式中的 ALL 更改为 ANY 就可以正常工作,它只是没有选择我想要的。我将在主项目之外创建一个测试项目并尝试重新创建相同的场景。也许这会阐明什么是错误的。
  • contactUid 转换为字符串是什么?您需要确保其中没有奇数字符。举个极端的例子,如果你将一个谓词格式保存为字符串,然后尝试将该字符串插入到谓词变量中,则会导致谓词无法初始化,因为谓词会尝试将字符串解析为它所代表的谓词,即字符如字符串中的=,>% 被解释为比较运算符而不仅仅是字符。
  • UID 字符串如下所示:10474FD7-2FC8-44AA-85FA-3C7BA0773AC4。我尝试使用不带破折号的格式(例如:1001595484),结果是一样的。我在示例应用程序中重新创建了该场景,无论 UID 格式如何,我都会遇到相同的崩溃。
【解决方案2】:

核心数据编程指南说

抓取和商店类型之间存在一些交互。在 XML、二进制和内存存储中,谓词和排序描述符的评估是在 Objective-C 中执行的,可以访问 Cocoa 的所有功能,包括 NSString 上的比较方法。另一方面,SQL 存储将谓词和排序描述符编译为 SQL,并在数据库本身中评估结果。

它继续描述了使用 NSPredicate 和 NSSQLiteStoreType 的一些其他限制,但您在此处使用“ALL”是一个(未记录的)限制,与 fetch 请求如何发出 SQL 有关。

在底层,CoreData 会为您的架构生成三个表:

  • 联系人表格
  • 组表
  • 关联它们的连接表

因此,当您调用 myGroup.contacts 时,会运行以下内容:

select * from Group join JOIN_TABLE on Group.pk == JOIN_TABLE.group_pk join Contact on JOIN_TABLE.contact_pk == Contact.pk where Group.pk == 12

一个点字符背后有很多事情发生!

无论如何,要真正满足您的查询,您需要这样的东西。我在一个实际的 SQLite CD 数据库上对此进行了测试,所以表名看起来很奇怪,但应该还是可以理解的:

select ZGROUP.Z_PK as outer_pk from ZGROUP where "myUID" not in 
(select ZCONTACT.ZUID as contact_uid from ZGROUP join Z_1GROUPS on Z_1GROUPS.Z_2GROUPS == ZGROUP.Z_PK join ZCONTACT on Z_1GROUPS.Z_1CONTACTS == ZCONTACT.Z_PK where ZGROUP.Z_PK == outer_pk)

我不是 SQL 专家,但我的观察首先是这个查询会很慢,其次它与我们开始使用的 NSPredicate 相距甚远。因此,只有通过大量的努力,CD 才能为您想要做的事情提供一个 SQL 查询,而它所提供的查询不会比 ObjC 中的幼稚实现好多少。

不管它值多少钱,Apple 开发人员说here SQLite 不支持 ALL,相反的文档是错误的。不过,该文档在 2013 年仍然存在,因此似乎没有人对此采取任何行动。

不管怎样,你实际上应该做的是这样的:

NSFetchRequest *fetchRequest = ...
NSArray *result = [moc executeFetchRequest:fetchRequest error:&err];
result = [result filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId]];

这将评估软件中的谓词。

【讨论】:

    【解决方案3】:

    Core Data 不支持聚合表达式。

    作为记录,上面的警告来自 (2013) NSExpression Class Reference,在the paragraph relative to Aggregate Expressions。事实上,某些运算符(ANY、NONE)在 SQL 中可翻译时是受支持的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-28
      • 2014-06-03
      相关资源
      最近更新 更多