NSFetchedResultsController OR 谓词在 iOS6 上为某些项目返回重复项

Posted

技术标签:

【中文标题】NSFetchedResultsController OR 谓词在 iOS6 上为某些项目返回重复项【英文标题】:NSFetchedResultsController OR predicate returning duplicates on iOS6 for certain items 【发布时间】:2014-02-24 19:45:04 【问题描述】:

我正在使用 NSFetchedResultsController 将不同电子邮件帐户的消息加载到表格视图和集合视图中。在 ios7 上一切都很好,在 iOS6 中一切都很好,除了我使用组合 OR 谓词。

现在我知道,如果 NSFetchedResultsController 匹配多个这些谓词,它将返回相同的项目两次。我的问题是消息实体仅匹配这些谓词之一,但返回的次数与谓词相同。

因此,出于某种原因,NSFetchedResultsController 说这些消息传递了所有谓词,但仅在 iOS6 上。它针对来自特定电子邮件帐户的每条消息执行此操作,但不针对来自其他帐户的消息。

如果我将 OR 谓词调整为不使用 ObjectID,则会返回唯一结果。但是,随着值的调整,我的 UI 将不再正确更新

在我的一生中,我似乎无法弄清楚这些实体究竟是如何为其他电子邮件帐户和文件夹传递谓词的。

我的导致重复的谓词构造如下。 Account -> messages 是 oneToMany 关系,文件夹 messages 是 manyToMany。

                NSMutableArray *predicates = [NSMutableArray array];
                for (CRMFolder *folder in comboFolder.folders) 
                    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isMarkedForDelete != YES AND isTopReferenceMessage = YES AND folders contains %@ AND account = %@", folder, folder.account];
                    [predicates addObject:predicate];
                
                fetchRequest.predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];

因此,如果我将其更改为像这样的 objectID,我会得到独特的结果:

                NSMutableArray *predicates = [NSMutableArray array];
                for (CRMFolder *folder in comboFolder.folders) 
                    NSArray *objectIDs = [folder.messages valueForKey:@"objectID"];
                    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isMarkedForDelete != YES AND isTopReferenceMessage = YES AND self in %@", objectIDs];
                    [predicates addObject:predicate];
                
                fetchRequest.predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];

但是如果我将这两者结合起来,我仍然会得到重复。

                NSMutableArray *predicates = [NSMutableArray array];
                for (CRMFolder *folder in comboFolder.folders) 
                    NSArray *objectIDs = [folder.messages valueForKey:@"objectID"];
                    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isMarkedForDelete != YES AND isTopReferenceMessage = YES AND folders contains %@ AND account = %@ AND self in %@", folder, folder.account, objectIDs];
                    [predicates addObject:predicate];
                
                fetchRequest.predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];

关于任何想法

1) 为什么这些实体会传递它们不应该传递的谓词?

2) 为什么这只发生在 iOS6 上?

3) 解决方法是什么?

我知道我可以使用returnsDistinctResults,但这需要将所有内容都转换为字典,目前这不是一个选项。

编辑

我刚刚做了一个测试,如果我排除了造成问题的帐户,结果是正常的。同样,我只是排除了所有其他帐户,没有重复。所以只有当这些谓词全部结合时才会发生这种情况。我真的怀疑获取的结果控制器中存在一些错误。

编辑 2

这是产生多个重复的提取的原始 CoreData 日志。请注意,有 3 条消息重复 9 次以获得 27 个结果。

CoreData:注解:sql连接获取时间:0.0528s CoreData:SQL:SELECT t0.Z_PK,t1.Z_14BLINDCARBONCOPYMESSAGES FROM Z_3BLINDCARBONCOPYMESSAGES T1 JOIN ZCRMADDRESS T0 ON t0.Z_PK = t1.Z_3BLINDCARBONCOPIES WHERE t1.Z_14BLINDCARBONCOPYMESSAGES IN(276,276,276,276,276,276,276,276,276, 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) 按 t1.Z_14BLINDCARBONCOPYMESSAGES ASC 订购 CoreData:注解:sql执行时间:0.0004s CoreData:注释:从连接表中预取数据库中的多对多关系“blindCarbonCopies”。有 0 行 CoreData: sql: SELECT t0.Z_PK, t1.REFLEXIVE FROM Z_14REFERENCEMESSAGES t1 JOIN ZCRMMESSAGE t0 ON t0.Z_PK = t1.Z_14REFERENCEMESSAGES WHERE t1.REFLEXIVE IN (276, 276, 276, 276, 276, 276, 276, 276, 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) 由 t1.REFLEXIVE ASC 订购 CoreData:注解:sql执行时间:0.0525s CoreData:注释:从连接表中预取数据库中的多对多关系“referenceMessages”。有 0 行 CoreData:SQL:SELECT t0.Z_PK,t1.Z_14CARBONCOPYMESSAGES FROM Z_3CARBONCOPYMESSAGES T1 JOIN ZCRMADDRESS T0 ON t0.Z_PK = t1.Z_3CARBONCOPIES WHERE t1.Z_14CARBONCOPYMESSAGES IN(276,276,276,276,276,276,276,276,276, 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) ORDER BY t1.Z_14CARBONCOPYMESSAGES ASC CoreData:注解:sql执行时间:0.0270s CoreData:注释:从连接表中预取数据库中的多对多关系“carbonCopies”。有 0 行 CoreData: sql: SELECT t0.Z_PK, t1.Z_14SENDERMESSAGES FROM Z_3SENDERMESSAGES t1 JOIN ZCRMADDRESS t0 ON t0.Z_PK = t1.Z_3SENDERS WHERE t1.Z_14SENDERMESSAGES IN ( 276, 276, 276, 276,276, 6,276,72 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) 由 t1.Z_14SENDERMESSAGES ASC 订购 CoreData:注解:sql执行时间:0.0007s CoreData:注释:从连接表中预取数据库中的多对多关系“发送者”。有 6 行 CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZEMAIL, t0.ZLABEL, t0.ZNAME, t0.ZPRIMARYADDRESS, t0.ZPROFILEPHOTO, t0.ZPROFILEPHOTOLASTCACHEDATE, t0.ZPROFILEPHOTOSOURCE, t0.ZCONTACT, t0.ZUSER FROM ZCRMADDRESS t0 WHERE t0.Z_PK IN (?,?,?) CoreData:注解:sql连接获取时间:0.0004s CoreData:注释:总提取执行时间:3 行 0.0007 秒。 CoreData:注释:使用密钥“发送者”预取。有3行。 核心数据: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZCONTENTID, t0.ZCONTENTTYPE, t0.ZDATA, t0.ZDATE, t0.ZEXCHANGEID, t0.ZFILEEXTENSION, t0.ZISLOCAL, t0.ZISTEMPORARY, t0.ZORIGINALFILENAME , t0.ZSIZEINBYTES, t0.ZTHUMBNAIL, t0.ZMESSAGE, t0.ZUSER 来自 ZCRMATTACHMENT t0 WHERE t0.ZMESSAGE IN (?,?,?,?,?,?,?,?,?,?,?,?,? ,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY t0.ZMESSAGE CoreData:注解:sql连接获取时间:0.0005s CoreData:注释:总提取执行时间:0 行 0.0007 秒。 CoreData:注释:使用关键“附件”预取。有 0 行。 CoreData: sql: SELECT t0.Z_PK, t1.Z_14REPLYTOMESSAGES FROM Z_3REPLYTOMESSAGES t1 JOIN ZCRMADDRESS t0 ON t0.Z_PK = t1.Z_3REPLYTOS WHERE t1.Z_14REPLYTOMESSAGES IN ( 276, 276, 276, 7, 276, 7, 276, 6, 6, 276, 6, 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) ORDER BY t1.Z_14REPLYTOMESSAGES ASC CoreData:注解:sql执行时间:0.0006s CoreData:注释:从连接表中预取数据库中的多对多关系“replyTos”。有 6 行 CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZEMAIL, t0.ZLABEL, t0.ZNAME, t0.ZPRIMARYADDRESS, t0.ZPROFILEPHOTO, t0.ZPROFILEPHOTOLASTCACHEDATE, t0.ZPROFILEPHOTOSOURCE, t0.ZCONTACT, t0.ZUSER FROM ZCRMADDRESS t0 WHERE t0.Z_PK IN (?,?,?) CoreData:注解:sql连接获取时间:0.0004s CoreData:注释:总提取执行时间:3 行 0.0006 秒。 CoreData:注释:使用键“replyTos”预取。有3行。 CoreData: sql: SELECT t0.Z_PK, t1.Z_14RECIPIENTMESSAGES FROM Z_3RECIPIENTMESSAGES t1 JOIN ZCRMADDRESS t0 ON t0.Z_PK = t1.Z_3RECIPIENTS WHERE t1.Z_14RECIPIENTMESSAGES IN (276, 276, 276,276,6,276,7,276,7,27 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) ORDER BY t1.Z_14RECIPIENTMESSAGES ASC CoreData:注解:sql执行时间:0.0006s CoreData:注释:从连接表中预取数据库中的多对多关系“收件人”。有 6 行 CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZEMAIL, t0.ZLABEL, t0.ZNAME, t0.ZPRIMARYADDRESS, t0.ZPROFILEPHOTO, t0.ZPROFILEPHOTOLASTCACHEDATE, t0.ZPROFILEPHOTOSOURCE, t0.ZCONTACT, t0.ZUSER从 ZCRMADDRESS t0 WHERE t0.Z_PK IN (?) CoreData:注解:sql连接获取时间:0.0004s CoreData:注释:总提取执行时间:1 行 0.0007 秒。 CoreData:注释:使用密钥“收件人”进行预取。有 1 行。 CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZENCRYPTEDBODY, t0.ZENCRYPTEDhtmlBODY, t0.ZENCRYPTEDPLAINTEXTBODY, t0.ZENCRYPTEDSUBJECT, t0.ZMESSAGE FROM ZCRMMESSAGEBODY t0 WHERE t0.ZMESSAGE IN (?,?,?,? ,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) CoreData:注解:sql连接获取时间:0.0011s CoreData:注释:总提取执行时间:3 行 0.0015 秒。 CoreData:注释:使用键“messageBody”预取。有3行。 CoreData:注释:总提取执行时间:27 行 0.1482 秒。

【问题讨论】:

重复项是什么意思? FRC 不会两次返回相同的元素。 是的,它使用多个谓词。它们是完全相同的 NSManagedObject,NSFetchedResultsController 多次返回相同的永久 ID 我也检查了SQL DB,只有一个条目 打开 Core Data 的 SQLite 调试以查看到底发生了什么会很有用,如下所述:nshipster.com/launch-arguments-and-environment-variables folders contains ... 是错误的谓词用法。 contains 仅对字符串值匹配有效。 【参考方案1】:

正如 Dan Shelly 所说,CONTAINS 在谓词中的使用不正确。正如他解释的那样,我相信它仅用于在另一个字符串中查找子字符串。请参阅predicate documentation。但是,为了您的目的,您可以改用子查询。

所以,我将继续进行修复。您可以将子谓词更改为以下内容:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isMarkedForDelete != YES AND isTopReferenceMessage = YES AND SUBQUERY(folders, $folder, $folder = %@).@count != 0 AND account = %@", folder, folder.account];

正如 Martin R. 所指出的,一个提取请求不应两次返回相同的元素。我不知道为什么会这样,如果确实如此,除非它是使用无效谓词的结果。

另外,请注意,由于提取请求不应返回重复项,returnsDistinctResults 的用法与听起来有点不同。你可以找到一个例子here。

【讨论】:

如此奇怪的包含大部分时间都返回了正确的结果。一定是在比较id什么的。将其更改为子查询确实消除了重复项。谢谢! 这很好奇。一定是因为这个原因,并且与数组和字符串的原始表示以及如何实现此 CONTAINS 有关。

以上是关于NSFetchedResultsController OR 谓词在 iOS6 上为某些项目返回重复项的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?