iOS集合和表 - 异步计算单元格值

时间:2013-08-12 01:33:16

标签: ios asynchronous uicollectionview

我一直在开发一个应用程序,它涉及一些相当繁重但可变的单元格内容计算。我接触了几种不同的方法,结果很糟糕。我终于找到了一个很好的解决方案,但我决定将其发布用于讨论和评论。

我的特殊问题是我有一个可以显示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:
        ...
    }
}

所以...意见?其他解决方案?

0 个答案:

没有答案