NSFetchedResultsController 在 NSFetchRequest 中带有 NSDictionaryResultsType

Posted

技术标签:

【中文标题】NSFetchedResultsController 在 NSFetchRequest 中带有 NSDictionaryResultsType【英文标题】:NSFetchedResultsController with NSDictionaryResultsType in NSFetchRequest 【发布时间】:2012-11-25 03:04:13 【问题描述】:

我从一个星期以来一直在研究这个问题,但没有任何解决方案,所以我想把这个问题更笼统地概括一下,也许它会帮助用户研究它并给我一个解决方案。

场景:

我有一个费用跟踪 ios 应用程序,我有一个名为“DashBoardViewController”的视图控制器(表格视图控制器 - 带有 FRC),它基本上可以对给定一周、一个月或一年的费用/收入进行分类,并将其显示为部分标题标题例如:(2012 年 10 月 1 日至 10 月 7 日),它根据特定的周或月或年显示费用/收入 ROWS 和相关内容。

我的问题:

我想要完成的是:

根据“Money”实体的“Category”属性显示不同的结果,并根据该属性计算“Sum”。

但是,我的名为“仪表板视图控制器”的视图控制器充满了 NSFetchedResultsController,其部分名称键路径为“类型”,可以是费用或收入。为了获得不同的结果,我将在我的 fetch 请求中使用 Result 类型作为 NSDictionaryResultsType ,这将给我唯一的结果,但 FRC 失败,它不适用。那么,我将如何获得我的部分名称呢?我已经发布了下面的代码。

编辑 - 基于马丁的建议

   - (void)userDidSelectStartDate:(NSDate *)startDate andEndDate:(NSDate *)endDate

    AppDelegate * applicationDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext * context = [applicationDelegate managedObjectContext];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Money" inManagedObjectContext:context];
    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    NSPredicate *predicateDate = [NSPredicate predicateWithFormat:@"(date >= %@) AND (date <= %@)", startDate, endDate];
    [fetchRequest setPredicate:predicateDate];


    // Edit the sort key as appropriate.

    typeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"type" ascending:YES];

    dateSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];

    if(self.choiceSegmentedControl.selectedIndex == 0)
    
        choiceSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"cat" ascending:NO];
    

    if(self.choiceSegmentedControl.selectedIndex == 1)
    
        choiceSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"vendor" ascending:NO];
    

    if(self.choiceSegmentedControl.selectedIndex == 2)
            
        choiceSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"paidBy" ascending:NO];
    

    NSArray * descriptors = [NSArray arrayWithObjects:typeSortDescriptor, dateSortDescriptor,choiceSortDescriptor, nil];

    [fetchRequest setSortDescriptors:descriptors];
    [fetchRequest setIncludesSubentities:YES];

    NSError * anyError = nil;

    NSArray *propertiesToFetch = [[NSArray alloc] initWithObjects:
                                  [entity.propertiesByName objectForKey:@"cat"],
                                  nil];

    [fetchRequest setReturnsDistinctResults:YES];
    [fetchRequest setResultType:NSDictionaryResultType];
    [fetchRequest setPropertiesToFetch:propertiesToFetch];

    NSArray * objects = [context executeFetchRequest:fetchRequest error:&anyError];

    for (NSDictionary *d in objects)
    
        NSLog(@"NSDictionary = %@", d);
    

    NSSet *uniqueSet = [NSSet setWithArray:[objects valueForKey:@"cat"]];

    uniqueArray = [NSMutableArray arrayWithArray:[uniqueSet allObjects]];

    self.categoryArray = uniqueArray;

    if(_fetchedResultsController)
    
        [_fetchedResultsController release]; _fetchedResultsController = nil;

    

    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:@"type" cacheName:nil];

    //self.fetchedResultsController.delegate = self; in order to stop "change tracking"

    if(![_fetchedResultsController performFetch:&anyError])
    
        NSLog(@"error fetching:%@", anyError);
    

    [fetchRequest release];

    //Finally you tell the tableView to reload it's data, it will then ask your NEW FRC for the new data
    [self.dashBoardTblView reloadData];

    self.startDate = startDate;
    self.endDate = endDate;



使用此代码,SECTION NAME KEY PATH 不起作用,它抱怨该对象将被放置在未命名的部分中。

【问题讨论】:

【参考方案1】:

获取的结果控制器不支持更改跟踪(即设置 FRC 委托)与带有 NSDictionaryResultType 的获取请求结合使用。

setIncludesPendingChanges: 函数记录了原因:

特殊注意事项

不支持同时使用 YES 结果类型为 NSDictionaryResultType,包括计算 汇总结果(例如最大值和最小值)。对于字典,数组 从 fetch 返回的反映了持久化中的当前状态 存储,并且不考虑任何待处理的更改、插入、 或上下文中的删除。

FRC 的更改跟踪意味着提取请求是 includesPendingChanges = YES,这不适用于 NSDictionaryResultType

一种解决方法可能是使用 FRC 不进行更改跟踪,因此您无需设置 FRC 委托。但这意味着要更新表格视图,您必须

保存托管对象上下文,然后 在 FRC 上调用 performFetch,在表格视图上调用 reloadData

另一种解决方法是使用 FRC 获取所有没有 sum 聚合的部分和行,并使用结果来计算内存中聚合的新表行(例如,在 controllerDidChangeContent 中)。

更新:(来自讨论)另一个重要的一点是,如果您使用带有NSDictionaryResultType 的提取请求,那么提取结果控制器的sectionNameKeyPath 必须包含在@987654330 中获取请求的@。

【讨论】:

那么,在上述方法中,您认为我应该怎么做才能使部分名称存在并通过聚合获取不同的结果。 您提到的第二种方法“使用 FRC 获取所有部分和行”,但它不会为我的类别提供不同的行。 @AngadManchanda:你为什么不尝试第一种方法,这可能更容易实现。 我刚刚编辑了我的答案。现在在获取部分时面临问题。我刚刚为此添加了代码。 由于 frc 委托为 nil,因此对于表视图,返回的节数现在始终为 1。我将如何管理?我还为表格视图数据源和委托方法添加了代码。

以上是关于NSFetchedResultsController 在 NSFetchRequest 中带有 NSDictionaryResultsType的主要内容,如果未能解决你的问题,请参考以下文章

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