Подтвердить что ты не робот

Можно использовать как reloadItemsAtIndexPaths, так и reloadData, если значение dataSource может измениться?

Я изучаю аварийную ошибку с 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.

4b9b3361

Ответ 1

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

Итак:

  • Вид коллекции считывается из источника данных A.
  • Сетевая запись в источник данных B.
  • В определенный момент, скопируйте B в одним махом.
  • Сообщить о просмотре коллекции.

Таким образом, представление коллекции никогда не должно беспокоиться о состоянии гонки - оно всегда читается из фиксированного набора данных, насколько это возможно.