多对多 NSPredicate 与 NSFetchedResultsController
Posted
技术标签:
【中文标题】多对多 NSPredicate 与 NSFetchedResultsController【英文标题】:Many To Many NSPredicate With NSFetchedResultsController 【发布时间】:2014-12-03 19:39:51 【问题描述】:我无法根据我的数据模型配置创建 NSPredicate 对象。
我有 2 个实体,一个叫 Bob,一个叫 Fred。 Bob 与 Fred 有一个称为“hasFreds”(反面是 hasBobs)的多对多关系,并且 Fred 有一个 BOOL 类型的属性 hasEaten。
我想获取 Bob 的列表,其中每个 Bob 至少有一个 Fred,hasEaten=YES。如果 hasEaten=YES 没有 Fred,我想返回所有没有 Fred 的 Bob。
这是我到目前为止所拥有的,但它并不完全有效(它不符合“如果没有带有 hasEaten=YES 的 Fred,我想返回所有没有 Fred 的 Bob”条件):
predicate1 = [NSPredicate predicateWithFormat:
@"(SUBQUERY(hasFreds, $fred, $fred.hasEaten = %@).@count > 0)"
,@YES];
fr = [NSFetchRequest fetchRequestWithEntityName:@"Bob"];
fr.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:NO selector:@selector(compare:)]];
fr.predicate = predicate1;
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fr managedObjectContext:[[self appDelegate] managedObjectContext] sectionNameKeyPath:nil cacheName:nil];
fetchedResultsController.delegate = self;
【问题讨论】:
【参考方案1】:我认为从概念的角度来看,您的条件并不是真正的 one 查询。您想要进行两个查询,一个基于另一个的结果。这并不是 SQL 查询的真正目的,更不用说对象图谓词了。
因此,我认为最简单、最直观的解决方案应该是拥有 两个 版本的获取结果控制器。
您可以通过动态更改谓词来创建另一个版本。 (我经常使用这种模式,例如用于搜索。)因为无论如何子查询都会导致不止一次访问持久存储,所以在为获取的结果控制器设置谓词之前,您可以只进行一次有效的提取来评估条件。
设置谓词后,如有必要,将其删除:
if ([context countForFetchRequest:request] == 0)
request.predicate = nil;
// continue creating your FRC with the request
请注意,countForFetchRequest
非常高效且不占用内存。
要更新,
self.fetchedResultsController = nil;
[self.tableView reloadData]; // or collection view
如果您需要利用 FRC 的缓存功能,按照上述模式使用两个独立 FRC 的解决方案也是可行的。
【讨论】:
我使用您的解决方案提出了类似的解决方案。在 controllerDidChangeContent 中,我正在检查是否需要使用 countForFetchRequest 切换谓词,然后根据需要进行更改。我通过告诉 NSFetchedResultsController 执行获取并重新开始来跟进更改。我会标记你的答案,因为它确实对我有很大帮助 嗯,它本质上是相同的解决方案,所以你应该接受这个答案。只是 FRC 的交付机制略有不同 - 并不是解决方案的关键点。【参考方案2】:感谢用户 Mundi,我能够想出一个解决方案。首先是实现一个名为 getCorrectPredicate 的方法。
-(NSPredicate *)getCorrectPredicate
NSPredicate *predicate1;
NSFetchRequest *fr;
predicate1 = [NSPredicate predicateWithFormat:
@"(SUBQUERY(hasFreds, $fred, $fred.hasEaten = %@).@count > 0)",@YES];
fr = [NSFetchRequest fetchRequestWithEntityName:@"Bob"];
fr.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"date" ascending:NO selector:@selector(compare:)]];
fr.predicate = predicate1;
if([[[self appDelegate] managedObjectContext] countForFetchRequest:fr error:nil] == 0)
// we have a none situation
self.predicateState = [NSPredicate predicateWithFormat:
@"hasFreds.@count=0"];
else
self.predicateState = predicate1;
return self.predicateState;
第二个是检查 NSFetchedResultsController 委托方法 controllerDidChangeContent 谓词是否发生变化。
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
[_expensesTable endUpdates];
[[[self frc] fetchRequest] setPredicate:[self getCorrectPredicate]];
[self performFetch];
【讨论】:
以上是关于多对多 NSPredicate 与 NSFetchedResultsController的主要内容,如果未能解决你的问题,请参考以下文章