我有自定义单元格的UITableView。细胞的高度是动态的,因为它取决于其内容。所以对于快速滚动,我决定缓存单元格的高度。我用这个函数预先计算所有细胞的高度:
-(void)preCalculateHeightForCellsAndReloadTableAfterThat {
NSLog(@"%s",__PRETTY_FUNCTION__);
__weak typeof (self) weakSelf = self;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
[MMStopwatchARC start:@"preCalculateHeightForCellsAndReloadTableAfter"];
for (NSInteger i = 0; i < [self.dataProvider.dataArray count]; i++) {
dispatch_group_async(group, queue, ^{
NSString *keyPoitrait = [NSString stringWithFormat:@"%d-%d-p", 0, i];
NSString *keyLandscape = [NSString stringWithFormat:@"%d-%d-l", 0, i];
NSNumber* heightCache = [heightCellCache objectForKey:keyPoitrait];
SETextViewCell *cell = (SETextViewCell *)[self tableView:self.tableView preparedCellForIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
[MMStopwatchARC start:[NSString stringWithFormat:@"preCalculateHeightForCells %@", keyPoitrait]];
if (heightCache == nil) {
CGRect screenBound = [[UIScreen mainScreen] bounds];
CGSize sizeFitContent = [cell.richTextView sizeThatFits:CGSizeMake(screenBound.size.width, 100000)];
CGFloat height = MAX(self.tableView.rowHeight, sizeFitContent.height);
[heightCellCache setObject:@(height) forKey:keyPoitrait];
}
heightCache = [heightCellCache objectForKey:keyLandscape];
if (heightCache == nil) {
SETextViewCell *cell = (SETextViewCell *)[self tableView:self.tableView preparedCellForIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
CGRect screenBound = [[UIScreen mainScreen] bounds];
screenBound = CGRectMake(screenBound.origin.x, screenBound.origin.y, screenBound.size.height, screenBound.size.width);
CGSize sizeFitContent = [cell.richTextView sizeThatFits:CGSizeMake(screenBound.size.width, 100000)];
CGFloat height = MAX(self.tableView.rowHeight, sizeFitContent.height);
[heightCellCache setObject:@(height) forKey:keyLandscape];
}
[MMStopwatchARC stop:[NSString stringWithFormat:@"preCalculateHeightForCells %@", keyPoitrait]];
});
}
// after all task in group finished
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[MMStopwatchARC stop:@"preCalculateHeightForCellsAndReloadTableAfter"];
[weakSelf.tableView setTableFooterView:nil];
[weakSelf.tableView reloadData];
});
}
我在here使用了Matt Maher的观察,以便计算此任务的耗时。运行后,Log中的结果如下所示:
-[ViewController preCalculateHeightForCellsAndReloadTableAfter]
-> Stopwatch: [preCalculateHeightForCells 0-0-p] runtime: [0.053755]
-> Stopwatch: [preCalculateHeightForCells 0-1-p] runtime: [0.100539]
-> Stopwatch: [preCalculateHeightForCells 0-2-p] runtime: [0.098142]
-> Stopwatch: [preCalculateHeightForCells 0-3-p] runtime: [0.062571]
-> Stopwatch: [preCalculateHeightForCells 0-4-p] runtime: [0.081574]
-> Stopwatch: [preCalculateHeightForCellsAndReloadTableAfter] runtime: [0.699024]
问题是为什么 Time-preCalculateHeightForCellsAndReloadTableAfter比preCalculateHeightForCells 大得多?
我将组添加到全局队列,因此任务必须运行并发日志,以便任务(计算每个高度的单元格)按顺序运行(就像串行队列一样)。
答案 0 :(得分:0)
preCalculateHeightForCells运行时(0.396581s)和preCalculateHeightForCellsAndReloadTableAfter运行时(0.699024s)的总时间之间的差异可能来自主队列执行时间。
您正在为每个preCalculateHeightForCells使用全局队列。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
...
dispatch_group_async(group, queue, ^{
完成所有任务后的通知将在主队列(主线程)上执行。
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
主队列上的任务将在下一个runloop执行时间安排。但我不确定延迟为何如此之长(0.699024s-0.396581s = 0.302443s)。通常延迟为1/60秒(0.0167秒)或更短。那个应用程序当时很忙吗?如果全局队列可用于执行任务,则具有全局队列的dispatch_group_notify可能会获得更好的结果。
// after all task in group finished
dispatch_group_notify(group, queue, ^{
[MMStopwatchARC stop:@"preCalculateHeightForCellsAndReloadTableAfter"];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView setTableFooterView:nil];
[weakSelf.tableView reloadData];
});
});