UITableView insertRowsAtIndexPaths, бросающий __NSArrayM insertObject: atIndex: ошибка "объект не может быть nil"

Я пытаюсь динамически вставлять элемент в свой табличный вид. В моем приложении есть раздел чата, где он отображает старые (загруженные с сервера до инициализации контроллера представления) сообщения в разделе 0 и отображает только отправленные/полученные сообщения (где он изначально нул) в разделе 1. Когда загрузка загружается, все "старые" сообщения загружаются и отображаются, никаких проблем нет. Проблема начинается, когда я пытаюсь вставить строки. Вот что я делаю:

  • Я сначала обновляю свой источник данных таблицы, добавляя дополнительный элемент: [newMessages addObject:newMessage]; (newMessage - это экземпляр моего настраиваемого объекта сообщения, а newMessages - мой источник данных). Я проверяю, что у моего источника данных теперь есть 1 элемент (который был добавлен до 0).
  • Затем я вызываю следующий код:

    [self.chatTableView beginUpdates];
    [self.chatTableView insertRowsAtIndexPaths:@[[NSIndexPath 
        indexPathForRow:newMessages.count - 1 inSection:1]] 
        withRowAnimation:UITableViewRowAnimationBottom];
    [self.chatTableView endUpdates];
    

Мое приложение разбивается на метод endUpdates, давая мне эту ошибку: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'

Я сразу же проверил, есть ли newMessage nil или нет, он не nil (и повторно проверил мой источник данных), поэтому источник данных не является проблемой. Я думал, что проблема indexPathForRow:newMessages.count - 1 может быть проблемой, и попробовал разные значения (count - 2, count, count + 1) на всякий случай, если я что-то упустил. В таких случаях я получаю еще одну ошибку: 'NSInternalInconsistencyException', reason: 'attempt to insert row 1 into section 1, but there are only 1 rows in section 1 after the update'. Ошибка говорит обо всем, поэтому проблема не в том, чтобы с indexPathForRow:newMessages.count - 1.

Я добавил точки останова в метод -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath, чтобы увидеть, когда он точно вызван и что он возвращает. Похоже, что метод вообще не вызывается, точка останова не попадает (он срабатывает, когда представление сначала загружается и загружает исходные данные правильно, и я не устанавливаю источник данных нигде, поэтому также связаны делегаты/источники данных правильно). Сразу же я проверил другие методы:

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 2;
}

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    if(section == 0){
        return @"Eski mesajlar";
    }else{
        return @"Yeni mesajlar";
    }
}

Эти методы возвращают правильные значения. Я поставил точку останова в -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView, чтобы увидеть, когда она вызвана. По-видимому, он вызван внутри метода [self.chatTableView endUpdates]; (как видно из стека вызовов потока/очереди), но затем [self.chatTableView endUpdates]; сразу же выдает ошибку, даже не введя метод -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. Я видел примеры (и некоторые другие), такие как:

Я читал ответы там, но никто из них не помог мне. Что я делаю неправильно?

Ответы

Ответ 1

Есть ли у вас tableView:estimatedHeightForRowAtIndexPath:? Я заметил аналогичную проблему в одном из моих собственных приложений, в котором используется NSFetchedResultsController, где приложение вылетает с тем же сообщением об ошибке при вызове tableView:endUpdates. Комментирование tableView:estimatedHeightForRowAtIndexPath: исправил его для меня.

Ответ 2

Что касается удаления/комментирования tableView:estimatedHeightForRowAtIndexPath:, это было не так, как я разрешил его, так как у меня не было этого метода для моего делегата.

Вместо этого я действительно установил tableView.estimatedSectionHeaderHeight = 10.; в мой viewDidLoad. Оказывается, просто комментируя эту строку, исключение больше не происходит, и тогда это работает так, как я ожидаю.

Это слишком плохо, потому что я не могу сказать, почему комментирование этого (или метод estimatedHeightForRowAtIndexPath:) исправляет его.

Ответ 3

Недавно я разрешил эту проблему, не комментируя estimatedHeightForRowAtIndexPath:. Конкретная проблема, которую я имел, заключалась в том, что в нашей таблице было много ячеек разных размеров, поэтому мы просто использовали средний размер в оценке. Исправление заключалось в том, чтобы лучше оценить размер вставленных ячеек.

Мое предположение состоит в том, что ошибка вызвана таблицей, пытающейся перейти к вставленной ячейке. По-видимому, ранее оцененное contentSize вызывает проблемы при прокрутке к вставленным ячейкам с высотой, которые были оценены слишком малыми.

Ответ 4

У меня была эта проблема и исправлена, не вставляя, если таблица пуста. Другими словами, если таблица не имеет строк, то не используйте insertRowAtIndexPaths. Вместо этого добавьте объект в свой массив или какой-либо источник данных, а затем вызовите [myTableView reloadData]

Ответ 5

Я считаю, что ваша ошибка находится в методе tableView:numberOfRowsInSection:. Метод insertRowsAtIndexPaths вызывает этот метод перед вызовом tableView:cellForRowAtIndexPath, и это не соответствует источнику данных, это может быть проблема.

Я вижу, что newMessages - это локальная переменная, поэтому он, вероятно, не синхронизируется с массивом, возвращающимся из метода tableView:numberOfRowsInSection:.

Чтобы устранить проблему, просто убедитесь, что tableView: numberOfRowsInSection возвращает правильный номер при выполнении обновлений.

Кроме того: даже если newMessages был равен нулю, метод count вернет 0 - Objective-C работает таким образом.