NSFetchedResultsController: как получить sectionName из отношения ко многим сущности

У меня есть базовая модель данных как таковая: ParentObject <--->> ChildObject

И ParentObject, и ChildObject имеют атрибут levelNumber как:

typedef enum {
    Primary,
    Secondary,
    Tertiary
} LevelNumber;

У меня также есть метод для преобразования номера уровня из int в строку как в ParentObject, так и в childObject:

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

   }

}

Теперь у меня есть FetchedResultsController в табличном представлении, в котором перечислены ParentObject. Что я пытаюсь получить в названии раздела:

  • Если ParentObject равно Secondary или Tertiary, покажите название раздела как Secondary или Tertiary.
  • Если ParentObject равно Primary, но любой из ChildObjects равен Secondary или Tertiary, укажите название раздела как Secondary или Tertiary.
  • Если ParentObject и все ChildObjects равны Primary, отображать название раздела как Primary

Это довольно просто, если бы мне нужно было только заглянуть в levelNumber из ParentObject, что-то вроде следующего:

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 или что-то еще?


person Devang    schedule 29.06.2013    source источник
comment
IMO Имейте отдельный атрибут sectionLevelNumber в вашей родительской объектной модели.   -  person Bala    schedule 29.06.2013
comment
devforums.apple.com/message/682121#682121 это может вам помочь.   -  person Bala    schedule 29.06.2013
comment
@Bala Если я правильно понимаю, атрибут sectionLevelNumber сам по себе не будет хранить какое-либо значение, но его геттер фактически проверит levelNumber из ParentObject, а также все ChildObjects и увидит, какой номер уровня вернуть. Это будет проблемой, потому что при первой выборке FRC ожидает, что значения будут сохранены в базе данных, а не получены во время выполнения.   -  person Devang    schedule 29.06.2013
comment
Мунди объяснил это ясно   -  person Bala    schedule 01.07.2013


Ответы (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 и посмотреть, есть ли какие-либо из его дочерних элементов в этом сохранении.

person Mundi    schedule 30.06.2013
comment
Хороший ответ! У меня есть несколько вопросов: 1) Если levelNumber прописан в keyPathsForValuesAffectingSectionIdentifier, нужно ли переопределять setLevelNumber? 2) Является ли @ в @children опечаткой или намеренно? 3) Действительно ли изменение levelNumber дочернего объекта приведет к аннулированию sectionIdentifier связанного родительского объекта? Возможно, было бы полезно переопределить setLevelNumber в ChildObject. Спасибо! - person Martin R; 30.06.2013
comment
1) Да, это хорошая практика и не наносит вреда. 2) Опечатка. Исправил. 3) Вы правы в принципе, но я бы побеспокоился о взаимозависимости сущностей. В идеале вы инкапсулируете эту функциональность только в родительскую сущность. - person Mundi; 30.06.2013
comment
Но с вашим нынешним кодом изменение levelNumber ChildObject не обновит sectionIdentifier связанного ParentObject. Поскольку дочерние элементы в keyPathsForValuesAffectingSectionIdentifier влияют только на добавление/удаление дочернего объекта в отношении дочерних элементов, но не на изменение уже связанного дочернего объекта (если я не ошибаюсь). - person Martin R; 30.06.2013
comment
Это верно. В конце концов, вам придется перейти к дочерней сущности. Лучше переопределить setLevelNumber. - person Mundi; 30.06.2013
comment
Это не работает полностью. Что следует использовать для request.sortDescriptors?. Если я использую только levelNumber из parentObject. Выдает ошибку: 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, но один из ChildObject второго ParentObject является Secondary. Я не могу использовать sectionIdentifier для сортировки, так как это временное свойство. - person Devang; 06.07.2013
comment
Проблема решена путем наличия другого промежуточного атрибута в parentObject, который обновляется при обновлении levelNumber либо parentObject, либо childrenObject, а затем объединения его с приведенным здесь ответом для получения желаемого результата. - person Devang; 06.07.2013