NSFetchedResultsController:如何从实体的多对多关系中获取sectionName

Posted

技术标签:

【中文标题】NSFetchedResultsController:如何从实体的多对多关系中获取sectionName【英文标题】:NSFetchedResultsController: How to get sectionName from to-many relationship of the entity 【发布时间】:2013-06-29 08:42:59 【问题描述】:

我有一个这样的核心数据模型: ParentObject <--->> ChildObject

ParentObjectChildObject 都有一个属性 levelNumber 为:

typedef enum 
    Primary,
    Secondary,
    Tertiary
 LevelNumber;

我还有一种方法可以在 ParentObject 和 childObject 中将级别编号从 int 转换为 string:

-(void) levelString

    switch(self.levelNumber)
   
      case Primary: return @"Primary";
      case Secondary: return @"Secondary";
      case Tertiary: return @"Tertiary";
      default: return @"Error";

   


现在我在一个列出ParentObject 的tableview 中有一个FetchedResultsController。 我想在部分名称中获得的是:

如果ParentObjectSecondaryTertiary,则显示该部分 命名为SecondaryTertiary 如果ParentObjectPrimary,但任何ChildObjectsSecondaryTertiary,则将部分名称显示为SecondaryTertiary李> 如果ParentObject 和所有ChildObjects 都是Primary,则显示部分名称为Primary

如果我只需要查看ParentObjectlevelNumber 就很简单了,如下所示-

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"ParentObject"];
NSSortDescriptor *levelNumSD = [NSSortDescriptor sortDescriptorWithKey:@"levelNumber" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObjects:levelNumSD, nil];

self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                    managedObjectContext:myContext
                                                                      sectionNameKeyPath:@"levelString"
                                                                               cacheName:nil];

我了解 FRC 中的限制是 SortDescriptor 的结果应以与希望显示的顺序相同的顺序返回结果。 我怎样才能在这里合并ChildObject 检查。它会是一种新的 SortDescriptor,还是别的什么?

【问题讨论】:

IMO 在您的父对象模型中有一个单独的属性 sectionLevelNumber devforums.apple.com/message/682121#682121 这可能对你有帮助。 @Bala 如果我理解正确,属性sectionLevelNumber 本身不会存储任何值,但它的getter 实际上会查看ParentObjectlevelNumber 以及所有ChildObjects 并查看返回哪个级别。这将是一个问题,因为在 FRC 的第一次提取中,它希望将值存储在数据库中,而不是在运行时派生。 蒙迪已经解释清楚了 【参考方案1】:

查看 Apple 的示例代码 DateSectionTitles,它解释了如何将日期作为部分,并且您也可以将其用于您的案例。您在titleForSection 中管理的字符串的实际显示,但您在数据库中保留了一个“原始”且可排序的属性,称为sectionIdentifier

在您的特定情况下,部分标识符就像 levelNumber 一样,只需返回所有子项中最高的 levelNumber 即可计算。

模式如下:

-(NSString*)sectionIdentifier 
   [self willAccessValueForKey:@"sectionIdentifier"];
   NSNumber *tmp = [self primitiveSectionIdentifier];
   [self didAccessValueForKey:@"sectionIdentifier"];

   if (!tmp) 
      NSNumber *childrenMax = [self valueForKeyPath:@"@max.children.levelNumber"];
      tmp = childrenMax.intValue > self.levelNumber.intValue ?
            childrenMax : self.levelNumber;
      [self setPrimitiveSectionIdentifier:tmp];
   

   return tmp;

如果实体发生变化,不要忘记重置它。

-(void)setLevelNumber:(NSNumber)newNumber 
   [self willChangeValueForKey:@"levelNumber"];
   [self setPrimitiveLevelNumber:newNumber];
   [self willChangeValueForKey:@"levelNumber"];

   [self setPrimitiveSectionIdentifier:nil];

最后确保相关数据发生变化时失效:

+(NSSet*) keyPathsForValuesAffectingSectionIdentifier 
   return [NSSet setWithObject:@"levelNumber"];

要监控任何子级的levelNumber 的变化,让父级监听NSManagedObjectContextDidSaveNotification 并查看其是否有任何子级在该保存中。

【讨论】:

好答案!不过我有一些问题:1)如果在keyPathsForValuesAffectingSectionIdentifier中注册了“levelNumber”,是否仍然需要覆盖setLevelNumber? 2) “@children”中的@ 是错字还是故意的? 3) 更改 ChildObject 的 levelNumber 是否真的会使相关 ParentObject 的 sectionIdentifier 无效?也许在 ChildObject 中覆盖 setLevelNumber 会很有用。谢谢! 1) 是的,这是一种很好的做法,没有任何害处。 2)错字。更正了它。 3)原则上你是对的,但我会担心实体的相互依赖性。理想情况下,您只将此功能封装到父实体中。 但是使用您当前的代码,修改 ChildObject 的 levelNumber 不会更新相关 ParentObject 的 sectionIdentifier。因为 keyPathsForValuesAffectingSectionIdentifier 中的“children”只会影响子对象对“children”关系的添加/删除,但不会修改已经相关的子对象(除非我弄错了)。 没错。毕竟,您将不得不去儿童实体。最好覆盖setLevelNumber 这并不完全有效。 request.sortDescriptors 应该使用什么?如果我只使用parentObjectlevelNumber。它给出了错误:CoreData: error: (NSFetchedResultsController) The fetched object at index 2 has an out of order section name '2(Secondary) Objects must be sorted by section name'。例如,当所有ParentObject 都是Primary,但第二个ParentObjectChildObject 之一是Secondary 时,就会发生这种情况。我不能使用sectionIdentifier 进行排序,因为它是一个瞬态属性。

以上是关于NSFetchedResultsController:如何从实体的多对多关系中获取sectionName的主要内容,如果未能解决你的问题,请参考以下文章

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