TableView系列--优化

优化UITabelView

内建方法

  1. 首先是重用cell/header/footer的单个实例,即便是我们需要显示多个。这是优化UIScrollView(UITableView的父类)最明显的方式,UIScrollView是由苹果的工程师提供的。为了正确的使用它,你应该只有cell/header/footer类,一次性初始化它们,并返回给UITableView。

但重要的事情是:在UITableView的dataSource中实现的tableView:cellForRowAtIndexPath:方法,需要为每个cell调用一次,它应该快速执行。所以你需要尽可能快地返回重用cell实例。

不要在这里去执行数据绑定,因为目前在屏幕上还没有cell。为了执行数据绑定,可以在UITableView的delegate方法tableView:willDisplayCell:forRowAtIndexPath:中进行。这个方法在显示cell之前会被调用。

但是UITableView的问题在哪?正如所解释的一样,UITableView不会同时维护所有cell的实例。相反,它只需要维护显示给用户的那些cell。

那么,UITableView是如何知道它的contentSize呢?它是通过计算所以cell的高度之和来计算contentSize的值。

UITableView的delegate方法tableView:heightForRowAtIndexPath:会为每个cell调用一次,所以你应该非常快地返回高度值。

很多人会犯一个错误,他们会在布局初始化cell实例并绑定数据后去获取它们的高度。如果你想优化滑动的性能,就不应该以这种方式来计算cell的高度,因为这事难以置信的低效,iOS设备标准的60 FPS将会降低到15-20 FPS,滑动会变得很慢。

AutoLayout

但如果是AutoLayout呢?它真的跟我所说的一样慢么?你可能会很惊讶,但这是事实。如果你想让你的App在所有设备上都能平滑的滚动,你就会发现这种方法难以置信的慢。你使用的子视图越多,AutoLayout的效率越低。

AutoLayout相对低效的原因是隐藏在底层的命名为”Cassowary“的约束求解系统。如果布局中子视图越多,那么需要求解的约束也越多,进而返回cell给UITableView所花的时间也越多。

哪一个更快呢:使用少量的值来执行基本的数学计算,还是找一个求解大量线性等式或不等式的系统么?现在想像一下,用户想要快速地滑动,每个cell的自动布局也执行着疯狂的计算。

使用内建方法优化UITableView的正确方法是:

重用cell实例:对于特殊类型的cell,你应该只有一个实例,而没有更多。
不要在cellForRowAtIndexPath:方法中绑定数据,因为在此时cell还没有显示。可以使用UITableView的delegate中的tableView:willDisplayCell:forRowAtIndexPath:方法。

我们需要更深一步

当然,上面提到的这些点不足以实现真正的平滑滚动,特别是当你需要实现一些复杂的cell(如有大量的渐变、视图、交互元素、一些修饰元素等等)时,这变得尤其明显。问题就不在布局了,而在渲染了

让我们把关注点放在UIView的opaque属性上。文档中说它用于辅助绘图系统定义UIView是否透明。如果不透明,则绘图系统在渲染视图时可以做一些优化,以提高性能。
他们可能没有最新的iPhone,所以cell必须快速地被渲染。比通常的视图更快。

渲染最慢的操作之一是混合(blending)。混合操作由GPU来执行,因为这个硬件就是用来做混合操作的(当然不只是混合)提高性能的方法是减少混合操作的次数。

在iOS模拟器上运行App,在模拟器的菜单中选择Debug,然后选中Color Blended Layers。然后iOS模拟器就会将全部区域显示为两种颜色:绿色和红色。
绿色区域没有混合,但红色区域表示有混合操作。

[图片上传失败...(image-415510-1523694499666)]
[图片上传失败...(image-d7e32d-1523694499666)]

正如你所看到的一样,在cell中至少有两处执行了混合操作,但你可能看不出差别来(这个混合操作是不必要的)。

优化UITableView中绘制数据操作的要点

  1. 减少iOS执行无用混合的区域:
    1.1 不要使用透明背景 (使用iOS模拟器或者Instruments来确认这一点;
    1.2 如果可以,尽量使用没有混合的渐变。
  2. 优化代码,以平衡CPU和GPU的负载。你需要清楚地知道哪部分渲染需要使用GPU,哪部分可以使用CPU,以此保持平衡。
    为特殊的cell类型编写特殊的代码。
  3. 减少子像素情况
    3.1 对所有像素相关的数据做四舍五入处理,包括点坐标,UIView的高度和宽度。
    3.2 跟踪你的图像资源:图片必须是像素完美的,否则在Retina屏幕上渲染时,它会做不必要的抗锯齿处理。

什么是子像素

在完美的世界中(我们尝试构建的),屏幕点总是被处理成物理像素的整型坐标。但在现实生活中它可能是浮点值,例如,线段可能起始于x为0.25的地方。这时候,iOS将执行子像素渲染

如果所有的平滑线段都使用子像素渲染技术来渲染,那你会让iOS执行一些不必要的任务,从而降低FPS。

什么情况下会出现这种不必要的子像素抗锯齿操作呢?最常发生的情况是通过代码计算而变成浮点值的视图坐标,或者是一些不正确的图片资源,这些图片的大小不是对齐到屏幕的物理像素上的(例如,你有一张在Retina显示屏上的大小为6061的图片,而不是6060的)。

在前面我们讲到,要解决问题,首先需要找到问题在哪。在iOS模拟器上运行程序,在”Debug
“菜单中选中”Color Misaligned Image
“。
这一次有两种高亮区域:品红色区域会执行子像素渲染,而黄色区域是图片大小没有对齐的情况。

Paste_Image.png

那如何在代码中找到对应的位置呢?我总是使用手动布局,并且部分会自定义绘制,所以通常找到这些地方没有任何问题。如果你使用Interface Builder,那我对此深表同情。

通常,为了解决这个问题,你只要简单地使用ceilf, floorf和CGRectIntegral方法来对坐标做四舍五入处理。就是这样!

异步UI

可能这看起来有点奇怪,但这是一种非常有效的方法。如果你知道如何做,那么可以让UITableView滑动得更平滑。

现在我们来讨论一下你应该做什么,然后再讨论下你是否可能这么做

✻ ✻ ✻ ✻ ✻

每个中等以上规模的应用都可能会使用带有媒体内容的cell:文本、图片、动画,甚至还有视频。

而所有这些都可能带有装饰元素:圆角头像、还#号的文本、用户名等。

我们已经多次提及尽可能快地返回cell的需求,而在这里有一些麻烦:clipsToBounds很慢,图片需要从网络加载,需要在字符串中定位#号,和许多其它的问题。

优化的目标是很明确的:如果在主线程中执行这些操作,则会让你不能很快地返回cell。

在后台加载图片,在相同的地方处理圆角,然后将处理后的图片指定给UIImageView。

立刻显示文本,但在后台定位#号,然后使用属性字符串来刷新显示。

在你的cell中,需要具体情况具体分析,但主要的思想是在后台执行大的操作。这可能不止是网络代码,你需要使用Instruments来找到它们。

记住:需要尽快返回cell。

✻ ✻ ✻ ✻ ✻

有时候,上面的所有技术可能都帮不上忙。如GPU仍然不能使用(iPhone4+iOS7)时,cell中有很多内容时,需要CALayer的支持以实现动画时(因为在drawRect:中实现起来真的很麻烦)。

在这种情况下,我们需要在后台渲染所有其它东西。此外它能在用户快速滑动UITableView时有效地提高FPS。

我们来看看Facebook的应用。为了检测这些,你可能需要往下滑足够的高度,然后点击状态栏。列表会往上滑动,因此你可以清楚地看到此时没有渲染cell。如果想要更精确,则不能及时获得。

这很简单,所以你可以自己试试。这时,你需要设置CALayer的drawsAsynchronously属性为YES。

但是我们可以检查这些行为的必要性。在iOS模拟器上运行程序,然后选择“Debug”菜单中的”Color Offscreen-Rendered“。现在所有在后台渲染的区域都被高亮为黄色。

优化的目标是很明确的:如果在主线程中执行这些操作,则会让你不能很快地返回cell。

在后台加载图片,在相同的地方处理圆角,然后将处理后的图片指定给UIImageView。

立刻显示文本,但在后台定位#号,然后使用属性字符串来刷新显示。

在你的cell中,需要具体情况具体分析,但主要的思想是在后台执行大的操作。这可能不止是网络代码,你需要使用Instruments来找到它们。

记住:需要尽快返回cell。

✻ ✻ ✻ ✻ ✻

这里是异步化UI的实现清单:
找到让你的cell
无法快速返回的瓶颈。
将操作移到后台线程,并在主线程刷新显示的内容。
最后一招是设置你的CALayer
为异步显示模式(即使只是简单的文本或图片)–这将帮你提高FPS。

结论

我尝试解释了iOS绘图系统(没有使用OpenGL,因为它的情况更少)的主要思路。当然有些看起来很模糊,但事实上这只是一些方向,你应该朝着这些方向来检查你的代码以找出影响滚动性能的所有问题。

具体情况具体分析,但原则是不变的。

获取完美平滑滚动的关键是非常特殊的代码,它能让你竭尽iOS的能力来让你的应用更加平滑。

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 日子 从时光的缝隙一点点流逝 仿佛还来不及细细凝视 女儿已是与我齐头并肩的 ...
    室幽人雅阅读 165评论 0 2
  • 昨天在图书馆看到一个征稿,主题是-------一封家书,觉得这个形式很好,对这个话题比较感兴趣,加之自己一年没回家...
    彭展望阅读 509评论 2 2
  • 1. 给我些许想念订购你的心房 从此 落户定居 几经风雨 未迁徙 2. 我得一种名叫想你的恶疾 只要念起你的姓名 ...
    卜默阅读 581评论 1 7