如果dataSource计数可能会改变,可以同时使用reloadItemsAtIndexPaths和reloadData吗?

时间:2015-12-10 18:30:07

标签: ios objective-c uicollectionview

我正在通过Crashlytics调查一个UICollectionView崩溃的bug,它通常采用以下形式:

  

致命异常:NSInternalInconsistencyException无效更新:   第0节中的项目数无效。包含的项目数   更新后的现有部分(25)必须等于数字   在更新(27)之前,该部分中包含的项目加上或   减去从该部分插入或删除的项目数量(1   插入,1删除)并加上或减去移入的项目数   或超出该部分(0移入,0移出)。

我认为这是因为我有一个collectionView,它定期用来自服务器的数据刷新自己,并且来自服务器的数据可能包含的项目多于或少于客户端UICollectionViewDataSource中包含的项目。

当我从服务器获取新数据时,我在我的集​​合视图上调用reloadData

但是,由于用户在网络下载完成之前与我的集合视图进行交互,我可能就在之前调用了reloadItemsAtIndexPathsreloadItemsAtIndexPaths似乎没有完成至少几百毫秒和许多处理器周期。因此,当在reloadItemsAtIndexPaths

的中间更新dataSource时,会发生此崩溃

reloadItemsAtIndexPaths是否有'直接'形式?或者我必须始终根据我的用例调用reloadData,这似乎会立即更新所有内容并使UICollectionView最终保持良好状态。

修改

根据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的同步。

1 个答案:

答案 0 :(得分:8)

竞赛条件总是复杂的问题,因为我相信你知道。如果我理解正确,你正在修改集合视图的数据源,而它仍在尝试重新加载自己,这意味着解决方案是保留一个单独的数据存储,并以原子方式复制到集合视图的数据源。 / p>

所以:

  • 集合视图从数据源A中读取。
  • 网络写入数据源B.
  • 在您指定的某个时刻,一举将B复制到A中。
  • 告诉集合视图重新加载。

这样,集合视图就不必担心竞争条件 - 它总是从一组固定的数据中读取。就其而言。