UITableView的优化

最近看到有些面试题中会问到TableView的优化,特定花了几天时间研究了一下各种优化技巧,主要就分为几个主要方向:

  • 从重用Cell的方面去优化
  • 从图层属性,圆角/阴影等方面去优化
  • 从UIView的绘制方面去优化
  • 从预计算和缓存高度,按需加载的方面去优化

从一个初学者写的卡到爆的TableView例子说起(引用自这里)

  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    ContacterTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ContacterTableCell"];
    if (!cell) {
    cell = (ContacterTableCell *)[[[NSBundle mainBundle] loadNibNamed:@"ContacterTableCell" owner:self options:nil] lastObject];
    }
    NSDictionary *dict = self.dataList[indexPath.row];
    [cell setContentInfo:dict];
    return cell;
    }
如果没有在xib中设置重用的标识,上面的cell就不会发生重用,只要TableView发生滚动,cellForRowAtIndexPath就会被调用,每一次cellForRowAtIndexPath被调用都会从NSBundle中获取View的示例再赋给cell。众所周知从NSBundle中读取数据是非常的慢的,所以这样写出的代码必定会卡到爆(我当初刚学iOS的时候就是这样写的(-_-))。

## 既然卡到爆就重用啰
首先注册cell

    table = UITableView(frame: self.view.bounds, style: UITableViewStyle.Plain)
    table?.registerClass(UITableViewCell.self, forCellReuseIdentifier: "mainViewControllerCellId")

然后重用

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("mainViewControllerCellId")
    // config cell data
    cell?.textLabel?.text = titles[indexPath.row]
    cell?.selectionStyle = .None
    return cell!
}
可以看到,使用了基本的重用机制后,整个TableView就变得流畅起来了,而且按照这种做法,cell的生产和使用就分开了,cellForRowAtIndexPath就只专注于设置cell的数据,不关心cell的初始化。一般的TableView界面采用这种写法都能获得不错的性能,而且有一定经验的iOS老手都是这样写的。

## 万一Cell中有圆角蒙版阴影透明这些图层属性呢
在UIView中,透明效果的渲染,圆角的渲染,蒙版的渲染,阴影的渲染会消耗很大部分的性能。在静态的View之中,这部分只消耗一次,不会造成太大的影响,但是在想UITableViewCell这些重用的动态的View中,每一次的重用都会消耗一次,所以总的性能消耗会很大。当然,要想在cell中使用这些属性,还是有优化的技巧来减少性能的消耗的。

#### 首先,尽量减少透明View。
UIView有一个属性```opaque ```,当不需要透明时可以将其设置为true,这样可以减少性能消耗。

#### 其次,减少离屏渲染
通常图层的以下属性将会触发离屏渲染:
* 阴影(UIView.layer.shadowOffset/shadowRadius/...)
* 圆角(当 UIView.layer.cornerRadius 和 UIView.layer.maskToBounds 一起使用时)
* 图层蒙板

对于以上这些,要减少离屏渲染比较好的方法是使用shadowPath来设置阴影,使用裁剪过的图片代替圆角而不是使用cornerRadius,图层模板能不用就不用。

#### 最后,合理使用光栅化
当然,除了第二点减少离屏渲染外,还有一点就是要合理使用光栅化。使用光栅化能够将能够有效提高性能,[把layer的shouldRasterize设为YES后,CALayer会被光栅化为bitmap,layer的阴影等效果也会被保存到bitmap中作为缓存。在使用了shadow或cornerRadius等效果时,缓存使性能得到提升](http://zhijiang.me/2015/08/03/%E5%BD%B1%E5%93%8D%E5%9B%BE%E5%BD%A2%E6%80%A7%E8%83%BD%E7%9A%84%E5%9B%A0%E7%B4%A0%E5%92%8C%E4%BD%BF%E7%94%A8Instrument%E8%BF%9B%E8%A1%8C%E6%A3%80%E6%B5%8B/)。

但是,使用光栅化要注意几点:

- 更新已经光栅化的CALayer会造成离屏渲染 (这最重要)
- 被光栅化的bitmap如果超过100ms没有被使用则会被移除
- 系统限制缓存的大小为2.5 x screen size

对于经常改动的,不易采用光栅化,否则会增加离屏渲染,增加性能消耗。对于相对静态的,建议采用光栅化。特别是界面比较复杂,动画比较复杂的,都建议使用光栅化。

## 糟糕我遇到了很复杂的Cell
由于绘制渲染复杂View的过程是非常耗时的,有时真的不排除遇到很复杂CellView的情况。由于本人对Core Graphics不太熟练,而且最近有点小忙,所以就不提供代码,只提供一些优化思路。
核心优化就两点:

1. 使用Core Graphics进行重绘,更进一步就是将所有subViews绘制成一张图片,通过消除View树的层级来提高效率。通俗来说就是所谓的扁平化。。。
通过重绘来压缩View树层级,能大大提高View的绘制效率,特别对于一些复杂界面效果特别明显。当然缺点就是使用重绘比较复杂,开发一些相对简单的界面可能会影响开发效率。

2.异步绘制渲染。
这个就是将View的绘制渲染放到后台线程中,完成后再通知主线程更新UI。这个思路跟Facebook的一个非常出名的开源框架[AsyncDisplayKit](https://github.com/facebook/AsyncDisplayKit)类似。

## 我想我的TableView更动人
如何更进一步优化TableView的体验?答案就是就两点:
- cell高度缓存和预计算
- 按需加载

对于第一点cell高度缓存和预计算,简单来说就是计算每一个cell的高度并缓存起来,避免重复计算。这里有个更合理的方法是[在用户不滑动TableView时,通过监听runloop来启动后台cell高度计算和缓存](http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/)。这样做的好处时将用户交互和耗时计算任务进行合理的分时,既不影响用户交互,也不浪费CPU计算性能。

对于第二点,用户滚动TableView的时候,希望能尽快看到当前屏幕显示的内容,而不关心其他还没有滚动到当前屏幕的内容。所以,一个比较有效的优化思路是优先绘制加载当前的内容,暂时忽略其他内容。根据这个思路,可以使用UIScrollView的协议方法,监听滚动的offset,然后当用户暂停滚动时的屏幕,就是需要优先加载的屏幕内容,这时候可以再去加载当前内容,而不是一开始按照滚动顺序全部加载。这样的话就不用特地设置一个网络请求的队列来管理网络请求,而且发出去了的请求也是cancel不了的。


## 总结
感觉对TableView的优化有了点思路,但是优化方法太多以至于还没消化过来,但是大的优化方向也是很清晰的:

- 能重用就重用
- 尽量减少view层级
- 使用异步绘制
- 使用缓存
- 按需加载
- 对于某些layer属性尽量采用替代方案

总之,这几天对这个专题的研究,收获很多,特定分享出来,写的不好,还望指教。

## 参考

[优化UITableViewCell高度计算的那些事](http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/)

[[iOS 程序性能优化](http://www.samirchen.com/ios-performance-optimization)](http://www.samirchen.com/ios-performance-optimization/)

[UITableView优化技巧](http://longxdragon.github.io/2015/05/26/UITableView%E4%BC%98%E5%8C%96%E6%8A%80%E5%B7%A7/)

[如何加强 iOS 里的列表滚动时的顺畅感?](http://www.zhihu.com/question/20382396)

[影响图形性能的因素和使用Instrument进行检测](http://zhijiang.me/2015/08/03/%E5%BD%B1%E5%93%8D%E5%9B%BE%E5%BD%A2%E6%80%A7%E8%83%BD%E7%9A%84%E5%9B%A0%E7%B4%A0%E5%92%8C%E4%BD%BF%E7%94%A8Instrument%E8%BF%9B%E8%A1%8C%E6%A3%80%E6%B5%8B/)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • 背景 UITableView作为iOS开发中最重要和常用的控件之一,其中的实现原理值得深入研究一下。苹果软件在这块...
    疯狂的Cracker阅读 708评论 2 4
  • 1. a.将cell及它的子控件设置为不透明的。b.尽量少用或不用透明图层。c.减少子控件的数量。d.尽量少用ad...
    BEYOND黄阅读 1,123评论 0 7
  • 简单的介绍 UITableView最核心的思想就是UITableViewCell的重用机制。简单的理解就是:UIT...
    面包与世界阅读 173评论 0 0
  • 9980r阅读 121评论 0 0
  • 目标:百分之九十以上的分数 第二天看书补完 中心词:实习 其实我才发现园艺,并不是农学。 园艺只是后花园的艺术 并...
    良辰美LiangChen阅读 228评论 0 0