使用聚合操作崩溃:Core Data iOS应用程序中的“ALL”

我正在一个iPhone应用程序,我有一个简单的多对多关系设置组和联系对象。 一个组可以有很多联系人,联系人可以属于多个组。

我尝试使用以下谓词select特定联系人不属于的所有组。 (注意:uid字段是我用来唯一标识联系人实体的string字段)

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

根据苹果的谓词编程指南,ALL聚合操作是有效的,但我得到以下exception,指出这是一个不受支持的谓词:

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

我可以使用一个类似的谓词来使用这个谓词来select一个联系人已经属于的所有组,所以看起来我具有正确定义的所有关系和字段。

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

构造谓词时抛出exception,而不是当我试图实际执行获取请求,所以它似乎与我正在使用的语法而不是核心数据支持相关。 我究竟做错了什么?

核心数据编程指南说

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

它继续描述使用NSSQLiteStoreType的NSPredicate的一些其他限制,但是您在这里使用“ALL”是一个(未logging的)限制,它与获取请求如何发出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数据库上testing了这个,所以表名看起来很奇怪,但它仍然是可理解的:

 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中的一个简单的实现好得多。

对于任何有价值的东西,苹果公司的开发人员都说在SQLite中不支持ALL,相反的文档是错误的。 那个文档在2013年仍然存在,所以似乎没有人对此做过任何事情。

无论如何,你应该做的是这样的:

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

这将在软件中评估谓词。

基本的语法是好的。 这个编译和运行在我的testing中:

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

contactUidvariables很可能是问题所在。 如果它没有值或者不能干净地转换为NSPredicate可以理解的string,那么会导致崩溃。 例如

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

…造成你描述的崩溃。

在将它分配给谓词之前,我会立即loggingcontactUid以查看它的实际string可转换值是什么。 它在NSLog中显示的方式是它将出现在谓词中的方式,因为它们都使用相同的string格式。

Core Data不支持聚合expression式。

有关logging,上面的警告是从(2013)NSExpression类参考,在相对于聚合expression式的段落中 。 事实上,有些运算符在SQL中可以被翻译的时候被支持(ANY,NONE)。