Сбой приложения при удалении строки

#iphone #uitableview #core-data #nsfetchedresultscontroller

#iPhone #uitableview #core-данные #nsfetchedresultscontroller

Вопрос:

Моя таблица используется для перечисления строк из основных данных. Все работает нормально, я использовал два типа данных для загрузки в одном и том же табличном представлении. Типы данных меняются из-за изменения области действия кнопки. Две кнопки области: одна отображает завершенные задачи, а другая — ожидающие выполнения. Я могу выполнять поиск, обновлять таблицу. Хотя редактируемый список данных доступен только для завершения и может быть удален, иногда он работает нормально, а иногда выдает ошибку как таковую,

 Serious application error.  An exception was caught from the delegate of 
NSFetchedResultsController during a call to -controllerDidChangeContent:.  *** -
[NSMutableArray insertObject:atIndex:]: index 4 beyond bounds for empty array with 
userInfo (null)
  

Контроллер результатов выборки инициализируется как,

 - (NSFetchedResultsController *)fetchedResultsControllerWithPredicate:(NSPredicate *)predicate{

if (self.fetchedResultsControl != nil) {
    NSLog(@"Here I am outside");

    return self.fetchedResultsControl;

}
  

Код для загрузки ячейки для tableview выглядит следующим образом :

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

static NSString *CellIdentifier = @"Cell";

CustomCell *cell =(CustomCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    NSArray *topLevelObjects=[[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:nil options:nil];
    for (id currentObject in topLevelObjects) {
        if([currentObject isKindOfClass:[UITableViewCell class]]){
            cell=(CustomCell *)currentObject;
            break;
        }
    }

}

[self configureCell:cell atIndexPath:indexPath];
return cell;
  

}

И метод обновления, вызываемый NSFetchedResultsControllerDelegate, имеет вид;

 - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.listingTable beginUpdates];
NSLog(@"Changed content");
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <  NSFetchedResultsSectionInfo>)sectionInfo
       atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

switch(type) {
    case NSFetchedResultsChangeInsert:
        [self.listingTable insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                      withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        [self.listingTable deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                      withRowAnimation:UITableViewRowAnimationFade];
        break;
}
  

}

 - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath {
NSLog(@"Changed content switch case");
UITableView *tableView = self.listingTable;

switch(type) {

    case NSFetchedResultsChangeInsert:
        [self.listingTable insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                         withRowAnimation:UITableViewRowAnimationTop];
        break;

    case NSFetchedResultsChangeDelete:
        [self.listingTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeUpdate:
        [self configureCell:(CustomCell *)[tableView  cellForRowAtIndexPath:indexPath]
                atIndexPath:indexPath];

        break;

    case NSFetchedResultsChangeMove:
      [self.listingTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
        [self.listingTable insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
        break;
}
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
NSLog(@"Changed Content");
[self.listingTable endUpdates];

}
  

Ответ №1:

Я не вижу никаких очевидных ошибок, но замена двух логических таблиц в одном tableview — опасная конструкция, потому что вы должны быть очень осторожны, чтобы tableview знал, что его логическая таблица была изменена.

Ошибка, которую вы получаете, предполагает, что tableview настраивает себя, используя один индекс одной логической таблицы, но на самом деле обращается к массиву второй логической таблицы, которая короче.

Ваше непосредственное решение — внимательно изучить такие методы, как numberOfSections и numberOfRowsInSection , чтобы убедиться, что они правильно обновляются при переключении логических таблиц.

Лучшее дизайнерское решение — использовать два отдельных табличных представления с их собственным источником данных, а затем менять их местами по мере необходимости.

Комментарии:

1. Я внедрил журнал в numberOfRowsInSection, но обновление правильное, и поэтому количество строк до удаления на 1 больше, чем после удаления.

2. Но также пытался распечатать управляемый объект для невидимых строк, и поэтому журнал выдает сообщение: <Memos: 0x71868e0> (сущность: Memos; идентификатор: 0x7150c10 <xcoredata://A66DA972-7A6E-4572-BF10-178A6B3B70AD/ Memos / p61> ; данные: <ошибка>). Но как только таблица прокручивается, удаление возможно. Это ошибка использования двух отдельных списков с одним и тем же fetchedresultscontroller и одним и тем же представлением таблицы.

3. Я не могу с уверенностью сказать, что проблема вызвана вашим дизайном, но я думаю, что это вероятно.

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

5. Это сработало бы в модели данных, но если вы попытаетесь отобразить их оба в одной таблице, вы получите те же проблемы.