Я изучаю аварийную ошибку с UICollectionView
через Crashlytics, которая обычно принимает эту форму:
Fatal Exception: NSInternalInconsistencyException Неверное обновление: недопустимое количество элементов в разделе 0. Количество элементов, содержащихся в существующий раздел после обновления (25) должен быть равен числу элементов, содержащихся в этом разделе, перед обновлением (27), плюс или минус количество элементов, вставленных или удаленных из этого раздела (1 вставлено, 1 удалено) и плюс или минус количество предметов, перемещенных в или из этой секции (0 перемещен, 0 удален).
Я считаю, что это вызвано тем, что у меня есть коллекцияView, которая периодически обновляется с данными с сервера и что данные с сервера могут содержать больше или меньше элементов, чем содержатся в клиенте UICollectionViewDataSource
.
Когда я получаю новые данные с сервера, я вызываю reloadData
в моем представлении коллекции.
Однако возможно, что из-за взаимодействия пользователя с моим представлением коллекции до завершения загрузки сети я вызвал reloadItemsAtIndexPaths
раньше. reloadItemsAtIndexPaths
, по-видимому, не заканчивается как минимум на несколько сотен мс и много циклов процессора. Следовательно, этот сбой, когда dataSource обновляется в середине reloadItemsAtIndexPaths
.
Есть ли "немедленная" форма reloadItemsAtIndexPaths
? Или я всегда должен называть reloadData
с учетом моего варианта использования, который, как представляется, обновляет все сразу и оставляет UICollectionView
в хорошем состояние в конце.
Edit
Вот что я сделал, за советом от TwoStraws:
// Prevent data source from batch updating while we work
self.dataSource.locked = YES;
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]];
} completion:^(BOOL finished) {
self.dataSource.locked = NO;
}];
Затем в моем классе источника данных, после получения результатов с сервера, я всегда вызываю assignResults
:
- (void)assignResults:(NSMutableArray *)newResults {
if (!self.locked) {
self.results = newResults;
[self.delegate handleDataSourceUpdated:self];
} else {
self.pendingResults = newResults;
}
}
- (void)setLocked:(BOOL)locked {
_locked = locked;
if (!locked && self.pendingResults) {
[self assignResults:self.pendingResults];
self.pendingResults = nil;
}
}
Как вы можете видеть, результаты получаются только в том случае, если источник данных не заблокирован; в противном случае они назначаются, когда источник данных разблокируется с помощью UICollectionViewController
. Обратите внимание, что все эти методы происходят в основном потоке, поэтому мне не нужно беспокоиться о синхронизации моего логического свойства, locked
.