TableView原理及深度优化

UITableView可以说是使用频率最高而且话题最多的UIKit组件了,他也是MVC的完美体现。

一. 原理篇

1.1 复用机制

UITableView是继承自UIScrollView,其最核心的思想就是UITableViewCell的复用机制。初始化的时候他会先创建cell的缓存字典和section的缓存array,以及一个用于存放复用cell的mutableSet。并且它会去创建显示的N+1个cell,其他都是从中取出来重用。

每当Cell滑出屏幕时,就会放入到set中(相当于一个重用池),当要显示某一位置的Cell时,会先去集合中取,如果有,就直接拿来显示;如果没有,才会创建。这样做的好处可想而知,极大的减少了内存的开销。

复用机制如下:

  • 维护一个重用队列

  • 当元素离开可见范围时,removeFromSuperview 并加入重用队列(enqueue)

  • 当需要加入新的元素时,先尝试从重用队列获取可重用元素(dequeue)并且从重用队列移除

  • 如果队列为空,新建元素

  • 这些一般都在 scrollViewDidScroll: 方法中完成

1.2 调用流程

初始化的时候内部会对_needsReload进行标记,之后调用setNeedsLayout方法。对于UIView的setNeedsLayout方法,在调用后Runloop会在即将到来的周期调用displayIfNeeded标记,如果为YES则会进行drawRect视图重绘。当RunLoop到来时,开始重回过程即调用layoutSubViews方法,且改方法是被重写过的。

它会在内部调用reloadData方法,在里面会对每个cell进行removeFromSuperview操作(为了指针悬挂的情况,有可能某个cell被其他视图引用),以及清除cell缓存字典和复用set。

之后会再次去更新缓存:他会先移除每个section的header和footer视图,然后根据DataSource中实现的delegate方法重新设置对应的参数,比如:titleForHeader,titleForFooter,heightForHeader,HeightForFooter,heightForRow,numberOfRows等。

重新缓存完成之后,那么就是进行布局更新了。他会先去获取容器视图相对于父视图的坐标以及偏移量,然后依次将header,cell,footer取出来添加到self上。

我们都知道,UITableView是继承自UIScrollView的,那么需要先确定它的contentSize及每个Cell的位置,然后才会把重用的Cell放置到对应的位置。所以UITableView的回调顺序是先多次调用tableView:heightForRowAtIndexPath:以确定contentSize及Cell的位置,然后才会调用tableView:cellForRowAtIndexPath:,从而来显示在当前屏幕的Cell。

比如:现在需要展示100个cell,当前屏幕显示10个。那么刷新tableview的时候,首先会调用100次tableView:heightForRowAtIndexPath:方法,然后调用10次tableView:cellForRowAtIndexPath: 方法。滑动屏幕时,每当Cell进入屏幕,都会调用一次tableView:heightForRowAtIndexPath:tableView:cellForRowAtIndexPath:方法。所以高度计算是一个很有必要优化的地方。


二. 优化篇

2.1 问题分析

  1. CPU(主要是主线程)/GPU负担过重或者不均衡(诸如mask/cornerRadius/drawRect/opaque带来offscreen rendering/blending等等)。由于所有的UIView都是由CALayer来负责显示,因此对Core Animation的了解就变得尤为重要。这里推荐Nick Lockwood的Core Animation: Advanced Techniques一书,其中有对Core Animation的性能有着非常详尽的梳理和剖析。
  2. Autolayout布局性能瓶颈,约束计算时间会随着数量呈指数级增长,并且必须在主线程执行。具体分析可以参考这篇文章:http://floriankugler.com/2013/04/22/auto-layout-performance-on-ios/。这也是为何ASDK抛弃了Autolayout而设计了自己的布局系统的重要原因之一(https://github.com/facebook/AsyncDisplayKit/issues/196)。Autolayout在单个View开发时能带来很多便利,而在一些需要高性能的场景下需要谨慎使用。
  3. 尽管从iPhone4S(A5)开始CPU已经采用多核,然而对于大多数app来说,并行效率仍然非常低下。也就是说,在app卡顿(主线程所占用的核心满负荷)时,往往CPU的其他核心几乎无事可做。一般情况下,由于主线程承担了绝大部分的工作,仅仅是把主线程的任务转移一部分给其他线程进行异步处理,就可以马上享受到并发带来的性能提升。这应该也是AsyncDisplayKit得名的原因之一。

2.2 优化思路

我们知道,当用户开始滚动或点击一个View,所有的事件都会被送到主线程等待处理。此时主线程能否抽出足够充裕的时间来处理变得极为重要,尤其是在连续操作(如UIGestureRecognizer)时,每次touchMoved事件处理都会占用主线程一定的时间(如新的UIImageView进入视图,主线程开始处理布局或者图片解码,而这些需要连续占用大量CPU时间)。如果一个操作耗时超过16ms(1000ms/60fps),那就意味着下一帧无法及时得到处理,引起丢帧。

2.3 可优化点

  1. 提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法。

  2. 复杂界面可采用异步绘制。

  3. 在大量图片展示时,可以滑动时按需加载。

  4. 尽量少用或不用透明图层,多个透明元素重叠显示可采用合并成一张图片显示。

  5. 减少subviews的数量,如果是不需要交互可以使用CALayer 替换掉 UIView。

  6. heightForRowAtIndexPath:中尽量不使用cellForRowAtIndexPath:

  7. 根据场景合理使用imageWithContentsOfFile和imageNamed。

  8. 页面元素多的时候,减少autolayout布局,采用frame。

  9. 缓存NSDateFormatter结果,不多次创建,及时释放。

  10. 图片解码时,CALayer 被提交到 GPU 前,CGImage 中的数据才会得到解码,GPU执行,卡主线程。常见的做法是在后台线程先把图片绘制到 CGBitmapContext 中,然后从 Bitmap 直接创建图片。

  11. CALayer 的 border、圆角、阴影、遮罩(mask)触发的离屏渲染,可开启CALayer.shouldRasterize ,转嫁到CPU上或是截图或者采用图片实现。

  12. 使用RunLoop和多线程在闲时处理一些繁重的计算工作。

具体可查看:UITableView+FDTemplateLayoutCell源码解析


参考文献:

优化UITableViewCell高度计算的那些事

AsyncDisplayKit介绍(一)原理和思路

微信读书 iOS 性能优化总结

iOS 保持界面流畅的技巧

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

推荐阅读更多精彩内容

  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,121评论 29 470
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • UITableView的简单认识 UITableView最核心的思想就是UITableViewCell的重用机制。...
    reallychao阅读 1,065评论 0 7
  • ​ 她是个很文静的女孩 我入学那会刚好是冬季招生,全部准备好后赶上春节,本来就没网没电视,(刚进宿舍什么都没办。)...
    辰小雨阅读 2,112评论 0 0
  • 这不是断章,简直是断片。 小强刚看完××的令人热血沸腾的演讲,特别想做一个追求快乐的人,追求生命意义的人。首先第一...
    formyself阅读 307评论 0 1