我一直在开发一个应用程序,它涉及一些相当繁重但可变的单元格内容计算。我接触了几种不同的方法,结果很糟糕。我终于找到了一个很好的解决方案,但我决定将其发布用于讨论和评论。
我的特殊问题是我有一个可以显示1到365个单元格的日历,每个单元格显示8个必须动态计算的值。 (一些有限的值缓存是可能的,但我很快发现数据完全缓存是不可能的。)在我尝试的最坏情况下,完整的计算链最多需要30秒,但随着更复杂的数据集,这会增加。显然,简单地计算创建单元格时的值会产生不可接受的延迟,更糟糕的是,无法响应的界面。 (当然,以下代码已被简化。)
- (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
switch (_typeOfLayout) {
case BACalendarLayoutTypeDay:
{
BAPeriod *period = [BAPeriod newWithSingular:_date];
BACalendarViewCell * cell = (BACalendarViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:BACalendarCellKind forIndexPath:indexPath];
cell.period = period;
BAPeriod * periodToDate = [[period preceding] union:period];
cell.value1 = [_modelInterface totalValue1:periodToDate];
cell.value2 = [_modelInterface totalValue2:periodToDate];
...
return cell;
}
case BACalendarLayoutTypeWeek:
...
}
}
我的第一个解决方案这个问题涉及将计算推迟到队列,但这需要非常仔细地同步计算链,这导致单元格显示然后更新我想要的方式。但是,它还会导致随机死锁,并且在显示结果之前会有更长的延迟。也有可能在计算值时,细胞可能已被移除。
- (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
switch (_typeOfLayout) {
case BACalendarLayoutTypeDay:
{
BAPeriod *period = [BAPeriod newWithSingular:_date];
BACalendarViewCell * cell = (BACalendarViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:BACalendarCellKind forIndexPath:indexPath];
cell.period = period;
BAPeriod * periodToDate = [[period preceding] union:period];
cell.valuesAvailableNow = false;
dispatch_async(_queue, ^{
cell.value1 = [_modelInterface totalValue1:periodToDate];
cell.value2 = [_modelInterface totalValue2:periodToDate];
...
cell.valuesAvailableNow = true;
[cell needsDisplay];
}
return cell;
}
case BACalendarLayoutTypeWeek:
...
}
}
我的下一个解决方案是忘记同步并在主队列上连续执行所有计算,但使用我创建的队列来管理计算。我也开始检查单元是否仍然可用,以防用户快速切换到另一个视图。结果是时间有所改善,但界面在显示后变得反应迟钝,花费很长时间用计算值更新单元格,并且由于某种原因有时无法更新单元格。
- (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
switch (_typeOfLayout) {
case BACalendarLayoutTypeDay:
{
BAPeriod *period = [BAPeriod newWithSingular:_date];
BACalendarViewCell * cell = (BACalendarViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:BACalendarCellKind forIndexPath:indexPath];
cell.period = period;
BAPeriod * periodToDate = [[period preceding] union:period];
cell.valuesAvailableNow = false;
dispatch_async(_queue, ^{
dispatch_group_t group = dispatch_group_create();
dispatch_group_asynch(group, dispatch_get_main_queue(), ^{
if ([self cellStillActive:cell]) {
cell.value1 = [_modelInterface totalValue1:periodToDate];
}
});
dispatch_group_asynch(group, dispatch_get_main_queue(), ^{
if ([self cellStillActive:cell]) {
cell.value2 = [_modelInterface totalValue2:periodToDate];
}
});
...
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
cell.valuesAvailableNow = true;
[cell needsDisplay];
}
return cell;
}
case BACalendarLayoutTypeWeek:
...
}
}
我意识到问题在于所有计算都被转储到主队列中,以便在一个实体块中执行,并且在它们之后添加了新事件。最终解决方案非常有效,是在主线程中添加执行SYNCHRONOUSLY的计算,而不是在前一个计算完成之前添加新计算。这允许在计算之间异步添加系统事件。结果是良好的界面响应性和出色的计算。
- (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
switch (_typeOfLayout) {
case BACalendarLayoutTypeDay:
{
BAPeriod *period = [BAPeriod newWithSingular:_date];
BACalendarViewCell * cell = (BACalendarViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:BACalendarCellKind forIndexPath:indexPath];
cell.period = period;
BAPeriod * periodToDate = [[period preceding] union:period];
cell.valuesAvailableNow = false;
dispatch_async(_queue, ^{
if ([self cellStillActive:cell]) {
dispatch_synch(group, dispatch_get_main_queue(), ^{
cell.value1 = [_modelInterface totalValue1:periodToDate];
});
}
if ([self cellStillActive:cell]) {
dispatch_synch(group, dispatch_get_main_queue(), ^{
cell.value2 = [_modelInterface totalValue2:periodToDate];
});
}
...
cell.valuesAvailableNow = true;
[cell needsDisplay];
}
return cell;
}
case BACalendarLayoutTypeWeek:
...
}
}
所以...意见?其他解决方案?