Почему NSFetchedResultsController не обновляется новыми данными?

Моя модель основных данных имеет две сущности: Автор и Книга с отношение ко многим (один автор->много книг). В главном представлении я отображаю список книг, где каждая ячейка содержит название книги и имя автора. Представление также разделено на разделы, где заголовок каждого раздела является именем автора. (обратите внимание, что "author.name" установлено как для дескриптора сортировки, так и для sectionNameKeyPath)

Вот код (упрощенный для ясности):

- (NSFetchedResultsController *)fetchedResultsController {

    if (__fetchedResultsController != nil) {
        return __fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Book" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"author.name" ascending:YES] autorelease];
    NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];

    [fetchRequest setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFetchedResultsController = [[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"author.name" cacheName:nil] autorelease];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    NSError *error = nil;
    [self.fetchedResultsController performFetch:&error];

    return __fetchedResultsController;
}    

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }

     Book* book = [self.fetchedResultsController objectAtIndexPath:indexPath];
     cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", book.name, book.author.name];
    return cell;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

    return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}

- (void)controllerDidChangeContent:(NSFetchedResultsController*)controller {

    [self.tableView reloadData];
}

Теперь, если пользователь изменит имя автора, а затем вернется к основному виду, в ячейках и разделах будет отображаться старое имя автора. После поиска в Интернете я нашел следующий код, который устраняет проблему со старыми именами авторов в ячейках, но не в заголовках разделов:

- (void)saveAuthorName:(NSString *)newName {
    for (Book* book in author.books) {
        [book willChangeValueForKey:@"author"];
    }

    author.name = newName;

    for (Book* book in author.books) {
        [book didChangeValueForKey:@"author"];
    }

    // save changes
    NSError * error ;
    if( ![self.moc save:&error] ) {
        // Handle error
    } 
}

Почему [разделы self.fetchedResultsController] все еще содержат старые имена авторов? Пожалуйста помоги!

Обновление №1

Этот раздел относится к ответу №1 Маркуса

Хммм, все еще немного неясно. Вы хотите сказать, что количество секций неверно?

Количество секций не изменилось. Неверное содержимое объектов в массиве свойств Sections.

Основываясь на опубликованном вами коде, вы просто извлекаете экземпляры NSManagedObject из NSFetchedResultsController.Может быть, есть некоторая путаница в том, что это такое?

В коде я извлекаю экземпляры NSManagedObject из NSFetchedResultsController для отображения названия книги и имени автора для каждой ячейки таблицы (когда cellForRowAtIndexPath называется). Однако заголовки каждого раздела в UITableView, насколько я понимаю, взяты не из NSManagedObject, а из объекта _NSDefaultSectionInfo, который реализует NSFetchedResultsSectionInfo. протокол (при вызове titleForHeaderInSection).

Я понял это по следующему коду, который я написал для отладки:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

    id mySection = [[fetchedBooks sections] objectAtIndex:section];
    NSLog(@"%@", mySection)

    return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}

Результат журнала был <_NSDefaultSectionInfo: 0x8462b90>. Документация NSFetchedResultsController для свойства Sections показывает:

/* Returns an array of objects that implement the NSFetchedResultsSectionInfo protocol.
   It's expected that developers use the returned array when implementing the following methods of the UITableViewDataSource protocol

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; 
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section;
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; 

*/

Поэтому, пожалуйста, поправьте меня, если я ошибаюсь: _NSDefaultSectionInfo не является NSManagedObject, верно? Если да, то как NSFetchedResultsController может обнаруживать изменения для объектов _NSDefaultSectionInfo при изменении Author NSManagedObject?

Таким образом, это приводит к нескольким вопросам:

Как вы меняете имя автора в этом другом представлении?

Код смены имени автора написан выше в saveAuthorName. Процесс в приложении выглядит следующим образом:

  • В главном UITableView выберите ячейку книги, которая открывает новое представление книги с помощью контроллера навигации.

  • В представлении книги выбор «Выбор автора» открывает новое представление «Выбор автора» с помощью навигационного контроллера. (все авторы перечислены в UITableView)

  • В представлении Select-Author выберите любого автора, который открывает новое представление Edit-Author с помощью навигационного контроллера.

  • В представлении Edit-Author изменение имени автора и сохранение, которое закрывает представление и возвращает предыдущее представление (Select-Author) в стек навигационного контроллера.

  • Теперь можно выбрать другого автора и отредактировать его и т.д.. до закрытия этого вида. (Открывается вид книги)

  • Закрытие вида книги приводит к основному виду, где отображаются все книги.

Имя автора в ячейке устарело или это только заголовок раздела?

Ячейка отлично обновляется именем автора (благодаря willChangeValueForKey и didChangeValueForKey, вызываемым в saveAuthorName). Только заголовок раздела старый.

Как выглядят ваши методы делегирования?

Уточните, пожалуйста, какой именно? Я написал все методы делегата, которые кажутся мне важными, в приведенном выше разделе кода. Сюда входят:

  • cellForRowAtIndexPath

  • titleForHeaderInSection

  • controllerDidChangeContent

Требуется ли какой-либо другой метод?

Вы уверены, что ваш -[UITableViewDatasource tableView: titleForHeaderInSection:] срабатывает после того, как вы вернетесь из редактирования?

На 100% уверен. titleForHeaderInSection приносит старые значения и вызывается после сохранения изменений.(cellForRowAtIndexPath также вызывается после сохранения изменений, но приносит новые значения)

Какие методы NSFetchedResultsControllerDelegate запускаются при возвращении?

Если вы имеете в виду при сохранении (имеется в виду после вызова saveAuthorName) вызываются следующие методы:

  • controllerWillChangeContent: (не используется, только для информации об отладке)

  • controller:didChangeObject: (не используется, только для информации об отладке)

  • controllerDidChangeContent:

Если вы имеете в виду при возвращении в главное представление ( означает закрытие представления книги) вызываются следующие методы:

  • cellForRowAtIndexPath

  • titleForHeaderInSection

  • numberOfSectionsInTableView

  • numberOfRowsInSection

Я ценю вашу помощь. Спасибо!

Обновление №2

Вы реализуете -controller: didChangeSection: atIndex: forChangeType:?

Да, но не срабатывает при смене имени автора. Текущая конфигурация для NSFetchedResultsController выглядит следующим образом:

  • Сущность: Книга
  • Дескриптор сортировки: author.name
  • sectionNameKeyPath: author.name

Изменение имени книги (а не автора name) вызовет событие didChangeSection, когда NSFetchedResultsController настроен следующим образом:

  • Entity: Book
  • Sort Descriptor: name
  • sectionNameKeyPath: name

Что означает, что делегат правильно подключен к NSFetchedResultsController.

Это выглядит как вызов [book willChangeValueForKey:@"author"] и [book didChangeValueForKey:@"author"], когда изменение имени автора недостаточно для NSFetchedResultsController для отслеживания изменений разделов.

6
задан Joshua 1 March 2012 в 22:21
поделиться