High Performance Auto Layout

前言


我们都希望自己的app能流畅运行不掉帧,这个topic介绍了

  • iOS12苹果都做了哪些针对流畅度的优化
  • 相关的底层运行机制
  • 怎样高效地使用自动布局
  • autolayout相关

iOS12的优化


首先,苹果在iOS12对autolayout做了性能优化,以下是几个参数对比,灰色是iOS11,蓝色是iOS12。

Performance benchmark

横轴是时间,可以看到Moving Tree的提升非常大,相信大家对于滑动列表时的卡顿(hiccup)都深有感触,以前我针对列表滑动这块也做了不少优化,这次更新iOS12后发现以前一些没有做优化的页面,有轻微卡顿的都不卡了,不过在一些性能敏感页还是有卡顿现象,还是需要自己去优化的。

Render Loop的运行原理


The render loop is the process that runs potentially at 120 times every second that make sure that all the content is ready to go for each frame.
———— by Ken Ferry

render loop是一个可以最多每秒运行120次的过程,用来保证每帧刷新前需要渲染的内容都已经准备好。

这个过程有三个阶段,
先Update Constraints,再Layout,最后Display。

Render Loop Workflow

Update Constraints从叶节点view开始执行,直到window;
layoutSubviews则是反过来,从window开始,传递到最终的叶节点view;
最后是drawRect绘制,也是从window开始;

苹果在设计上为了减少布局的重复调用,分了这3个阶段,并提供了平行的类似功能的方法,比如updateConstraints和layoutSubviews,setNeedsUpdateConstraints和setNeedsLayout等

Render Loop Methods

看到这里我感觉有必要重温一下这些布局方法,这里就简单回顾一下Update Constraints的3个方法

updateConstraints()

一般来说要改变某个约束,我们可以在某个action或方法中改变约束就可以了,但我们也可以重写某个view的updateConstraints,在重写的updateConstraints里集中改变一批约束。
updateConstraints通常是布局有变化时,系统给我们调用的,也可以由setNeedsUpdateConstraints触发,由于调用在render loop的第一阶段,他有两个适用场景

  1. 当我们有许多约束要产生变化,比如横竖屏切换时,我们可以在traitCollectionDidChange里调用setNeedsUpdateConstraints,updateConstraints里集中处理需要变化的约束
  2. 当我们希望尽早产生这些变化

要注意的问题

  1. 这个方法有可能每秒调用120次,所以要避免churning constraints,也就是不要deactivate所有约束,再重新activate他们;只改变需要改变的约束,对于不变的约束只添加一次
  2. 方法内不要调setNeedsUpdateConstraints,会产生feedback loop,虽然不是死循环但也会降低性能
  3. 在最后一行调用[super updateConstraints],保证向父view传递
setNeedsUpdateConstraints()

这个方法用来标记当layout即将发生时,让系统调用updateConstraints。
通常我们用它来优化批量的约束变化,这样可以避免重复计算。调用后并不会马上调updateConstraints,而是在下一次render loop的layout即将发生时,调用updateConstraints

updateConstraintsIfNeeded()

当有layout变化时,系统会调用这个方法,更新当前view和他的subviews的所有约束,也可以由我们主动调用,来获得最新的布局,和layoutIfNeeded类似。
禁止重写这个方法。调用后也不会触发updateConstraints。

另外还提到了如果使用interface builder布局,能避免踩到一些降低性能的坑,但我觉得最好还是用纯代码更灵活些。

以下是会降低性能的坑:

deactivate all再activate导致性能很差
上面的做法和这种类似,显然性能很差

好了,我们可以了解一下当激活约束时,到底发生了什么?

激活约束时的workflow

Update Phase
view通常是在一个window里,当update Constraint phase开始时, window会实例化一个布局引擎(layout engine),当给view添加约束时,会将约束对应的等式(equation)

firstItem.firstAttribute {=,<=,>=} secondItem.secondAttribute * multiplier + constant

交给布局引擎,布局引擎通过代数运算解出view布局需要知道的相应变量的值,如minX、minY、width、height。
其实就是最简单的代数里的解方程。。。如下

把text1的变量代入,解出text2的变量,以此类推

解完后会通知view,有value改变了,
view接着调用 [superView setNeedsLayout],让自己进入layout phase;

准备进入Layout Phase

Layout Phase
view会将engine里计算好的variable值 copy到自己的frame,subview再调用setBounds,setCenter,完成布局;

Layout

所以每一次在updateConstraints方法里deactivate constraints,再reactivate都会重复 实例化布局引擎-解方程-销毁的过程,有可能每秒执行多次,从而导致性能问题;

正确的做法应该是这样

保证只执行一次

如何高效地使用自动布局


don't pay for what you don't use
为了性能考虑,不是同一个父view下的view是不应该相互产生约束关系的(虽然可以这么做),因为会显著增加布局引擎的计算复杂度(类似于多个光源在多个物体上产生阴影),而目前iOS12里计算复杂度随view增加是线性增长的;
所以也不要写重复约束和没有用的约束;

don't wedge two layouts into one set of constraints
在一个视图里别搞两套布局关系,如下图

看着脑壳疼

总结:我们使用传统的frame布局就是一个布局、绘制、渲染的过程,使用自动布局其实就多了一个布局引擎运算。视频里特别提到用约束来布局并不会比用frame布局消耗太多的性能,因为engine的计算优化了,过程很高效,所以不要避免使用autolayout,你可以频繁地使用它,但不要添加多余或重复的约束。

和autolayout有关的东西


Inequality(也就是>=和<=)会消耗更多性能吗,和等号相比非常小,只是多了一个变量而已;
setConstant 做了最大程度的优化,鼓励使用;
priority 只是告诉引擎一个minimize error,对性能影响不大;

Instrument增加了新的分析layout性能的工具(一片掌声)
然而目前beta 版里没有...

前瞻,可以看到layout占用的CPU、发生churning的view数量等
关于churning

关于intrinsicContentsize

intrinsicContentsize给UIView添加约束

假如你写死宽高,不需要intrinsicContentsize,可以告诉引擎不用计算这个view的intrinsicContentSize,优化性能。
当然也可以返回一个指定的size,都需要重写,做法如下

swift版
OC版

关于systemLayoutSizeFittingSize
和intrinsicContentsize相反,他是从布局引擎获取size
每次调用该方法都要创建和销毁一个布局引擎,所以比较耗性能,不应该频繁调用。

systemLayoutSizeFittingSize workflow

关于约束冲突(Unsatisfiable Constraints)
会降低性能,也可能掩盖其他的布局问题,所以一定要解决!

视频地址:
https://developer.apple.com/videos/play/wwdc2018/220/

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

推荐阅读更多精彩内容