获取NSFetchedResultsController,NSSortDescription和sectionNameForKeyPath以协同工作

时间:2011-03-20 08:16:50

标签: objective-c ios core-data nsfetchedresultscontroller

我目前正在开发一个具有几个实体和关系的应用程序,如下所示:

商品<< - >的类别即可。

我目前正在提取实例,并使用项目category.name在各个部分中显示它们。在这种情况下,我可以使用排序描述符按名称对类别进行排序,这非常简单,工作正常(下面的相关代码):

-(NSFetchedResultsController*)fetchedResultsController {
    if (fetchedResultsController_ != nil)
        return fetchedResultsController_;
    NSManagedObjectContext *moc = [order_ managedObjectContext];
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:moc];
    [fetchRequest setEntity:entity];
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"category.name" ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [fetchRequest setSortDescriptors:sortDescriptors];
    [sortDescriptors release];
    [sortDescriptor release];
    NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
                                          initWithFetchRequest:fetchRequest
                                          managedObjectContext:moc
                                          sectionNameKeyPath:@"category.name"
                                          cacheName:nil];
    controller.delegate = self;
    self.fetchedResultsController = controller;
    [controller release];
    [fetchRequest release];

    NSError *error = nil;
    if (![fetchedResultsController_ performFetch:&error]) {
        // Error handling
    }
    return fetchedResultsController_;
}

我现在的问题是,我需要不是按名称对这些类别进行排序,而是通过属于类别实体的(NSNumber*) displayOrder属性进行排序。 但是我需要在tableview上显示章节标题才能继续使用类别name

如果我将sortDescriptor设置为使用category.displayOrder并将sectionNameKeyPath保持为category.name,则部分标题可以正常工作,但fetchedDcriptor只是被fetchedResultsController忽略,表部分按类别名称排序(不知道为什么??)。

我的下一个想法是覆盖displayOrder getter方法,但这并没有让我太过分,因为返回类型不同,而且我需要实际的displayOrder值来进行节排序。

所以现在我有一个感觉有点笨重的解决方案(下面的代码),我想知道是否有更好的方法来单独使用fetchedResultsController来实现同样的事情。

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
// The code below grabs a reference to first object for a given section
// and uses it to return the associated category name
{
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    NSArray *menuItems = [sectionInfo objects];
    if ([menuItems count] > 0)
    {
        MenuItem *menuItem = [menuItems objectAtIndex:0];
        NSString *categoryName = menuItem.category.name;
        return categoryName;
    }
    return [sectionInfo name];
}

我错过了一些基本的东西吗?

提前感谢您的想法。

罗格

4 个答案:

答案 0 :(得分:1)

就排序而言,the documentation有这样的说法:

  

如果控制器生成节,则数组中的第一个排序描述符用于将对象分组为多个节;它的键必须与sectionNameKeyPath相同,或者使用其键的相对排序必须与使用sectionNameKeyPath匹配。

用英语(你可能已经知道这个Rog了,但是你可能不会再肯定那些后来搜索它的人可能会理解这个解释),这意味着如果你正在使用部分,那么NSFetchRequest上的排序必须对所有项目进行分组在同一部分一起。这可以通过使第一个排序标准为用作节名称的字段,或者可以通过使第一个排序标准成为导致相同类别分组的其他标准。

文档没有说明如果搞砸会发生什么事情;它可能会完全弄乱部分名称,重复部分,跳过部分,检测情况并“修复”您的排序,甚至只是崩溃。您的任何类别都有相同的displayOrder吗?

您的解决方案当然是可行的,如果您无法通过displayOrder正确排序,同时按category.name标题部分,则可能是您的最佳解决方案。

答案 1 :(得分:1)

为什么要提取Item而不是Category?如果我正确理解您的关系,Category拥有与Item的1对多关系,那么理论上Category个实例应该有一个'items'属性,每个Item都会返回在那个类别。

如果是这种情况,那么您只需获取所有类别,然后按displayOrder对其进行排序。然后,忘记使用NSFetchedResultsController本身中的部分。相反,您关联的tableView方法看起来像:

- (NSInteger)numberOfSections {
    return [[self.fetchedResultsController fetchedObjects] count];
}

- (NSInteger)numberOfRowsInSection:(NSInteger)section {
    Category* category = [[self.fetchedResultsController fetchedObjects] objectAtIndex:section];
    return [[category items] count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    Category* category = [[self.fetchedResultsController fetchedObjects] objectAtIndex:section];
    return category.name;
} 

简而言之,我认为您通过提取Item而不是Category来过度复杂化,并尝试让NSFetchedResultsController管理您的部分分组。它更简单,只需要更少的代码就可以自己进行部分管理。

答案 2 :(得分:1)

这是一个非常好的解决方案,Rog。

您当然不希望/需要子类NSFetchedResultsController。

@ aroth,我们没有足够的信息来了解他的对象模型的细节,但这些名称肯定意味着类别不属于项目。物品有一个类别。他的目的是显示一个项目列表,这就是他提取物品的原因。

答案 3 :(得分:0)

您可能需要子类化NSFetchedResultsController并自定义节名称函数。请参阅子类化的NSFetchedResultsController类文档。