UITableView 性能优化 -- 《iOS 性能优化实战》读书笔记1

UITableView 的加载原理

可变高的列表载体 Cell 是在开发中经常处理到的一个技术点。
UITableViewCell 的高度需要在数据源代理中设置:

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return height;
}

heightForRowAtIndexPath方法会重复执行很多次,并且heightForRowAtIndexPath方法的执行机制在不同版本 iOS 系统中还会有很大不同。在 iOS 11 中,默认已经采取了一些优化手段。

在 iOS 9上,要显示一行 cell,至少要执行 5 遍heightForRowAtIndexPath方法:

  • UITableView 配置部分
    • 当 UITableView 视图即将被展示在屏幕上时,会拉取所有行高数据
    • UITableView 在执行setLayoutMargins方法进行自身布局时会拉取所有行高数据
    • UITableView 在执行layoutSubviews方法进行子视图布局时会再次拉取所有行高数据
  • UITableViewCell 配置部分
    • 当使用 CellID 获取与 UITableView 绑定的 cell 时会拉取本行 cell 的高度数据
    • 当 cell 调用 layoutSubviews 方法急性布局时会再次拉取本行 cell 的高度数据

在上面列举的 5 种拉取 cell 高度数据的场景中,UITableView 配置部分只会在 UITableView 第一次展现在屏幕上时出现,但是它拉取的是所有行的行高数据,如果表视图有 100 行或者更多,那么这就是一个非常耗能的过程。

UITableViewCell 的配置部分,只有当 cell 将要出现在屏幕上时才会出现,并且只拉取当前 cell 的行高,这两种场景会在用户滑动 UITableView 时不断被执行,并且根据 UITableView 的 cell 布局原理,系统会默认准备比当前一屏高度所能容纳cell 的个数多 1 个 cell。

当执行reloadData方法进行界面刷新时,系统先会把所有行的行高数据拉取一遍,之后和 UITableViewCell 配置部分的场景一致,会拉取即将出现在屏幕上的 cell 的行高数据。下面是UITableView 的加载原理示意图:

UITableView 加载原理图

通过以上分析,以 10 行数据的视图为例,若一屏幕可以呈现 7 行数据(UITableView 需要准备 8 行),则在第一次展示 UITableView 视图时,会执行 44 次heightForRowAtIndexPath,每次刷新 UITableView 需要执行 24 次heightForRowAtIndexPath,如果 UITableView 行数增加的三位数,这个方法的执行次数将会非常惊人。

可变行高的优化方式

如果将复杂的计算代码写在heightForRowAtIndexPath中,代价是巨大的。滑动不流畅、屏幕卡顿等很多性能问题都是由于这个原因。对于行高固定的表格视图,可以直接设置 UITableView 的固定行高,如果行高是不固定的,则应该想办法让heightForRowAtIndexPath方法完成最少的工作。

其实最少的工作莫过于拿一个高度直接返回,因此通常会将对应行的行高计算一次后,把值保存起来,之后在执行heightForRowAtIndexPath拉取行高时,直接返回。具体操作比较灵活,可以对应一个数组属性,将计算后的行高放入数组,在每次取行高时,检查数组中是否有已经计算过的行高数据,如果有直接返回。一种更好的方式是将行高数据封装进 cell 的数据模型 Model 中。

然而,这只是提高了代码性能,工作量和复杂度有增无减。iOS 7 之后,可以使用estimateRowHeight属性,iOS 11 中,系统已经默认定义这个值为 44 来进行列表视图的性能优化。这个值设置 cell 的大约行高。设置estimateRowHeight无需再设置rowHeight,也不需要实现heightForRowAtIndexPath,系统会根据 cell 中的 contentView 的约束来计算自己的行高。estimateRowHeight用于 UITableView的初始化,会影响到表格视图右侧滚动条的宽度。当 cell 展现出来时,真正的行高并不受这个属性值的影响。

So,问题来了:如何让cell 正确计算自己的高度?
答案是使用 AutoLayout,给 cell 布局足够的压力,让 contentView 的上下左右必须被内部控件的约束撑满。

Note:cell 的子视图必须添加到 contentView 上,否则计算时会出现问题

这是性能最优的列表渲染方式。

上面说预估行高会影响到 UITableView 右侧滚动条的展示,如果每个 cell 行高跳跃跨度比较大,则滚动条宽度的配置会失准,随着用户滑动,右侧滚动条可能会出现长短跳跃的情况,如果想要精准这个滚动条的配置,可以在如下代理方法中返回具体 cell 的估计行高:

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    //这里根据不同分区或者不同行设置估计的行高
    return 44;
}

关于estimatedHeightForRowAtIndexPath这个方法还有一种应用场景:
对于没有使用自动布局、cell 的高度需要手动计算的场景,如果实现了这个方法,并且实现了heightForRowAtIndexPath方法,那么heightForRowAtIndexPath方法会以懒加载的方法执行,只有在 cell 将要被展现在屏幕上时才会被执行,这也可以有效减少由于高度计算带来的性能负担。

Note: UITableViewCell在被创建出来时,宽度并不一定和 UITableView 的宽度一致,如果需要获取 cell 的绝对宽度来处理逻辑,那么需要在 cell的 layoutSubviews 方法里面进行,此时 cell 的宽度才正确

高度不固定的列表分区头、尾视图

一般头尾高度固定,不考虑它们带来的性能问题。对比 cell 的布局原理,也可以使用自动布局实现自适应高度。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容