多对多 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的主要内容,如果未能解决你的问题,请参考以下文章

CoreData:使用 NSPredicate 过滤一对多对多关系(此处不允许错误对多键)

NSPredicate 用于多对多关系

用于多对多关系的 NSPredicate

具有多对多关系的核心数据 NSPredicate

核心数据 - 从多对多关系构建 NSPredicate

具有多对多关系的 NSPredicate