1.Auto Layout来历
先看两个时间点:
- 1997年AutoLayout用到的布局算法Cassowary被发明出来
- 2011年,Apple将Cassowary算法用到了自家的布局引擎AutoLayout中
Cassowary 能够有效解析线性等式系统和线性不等式系统,用来表示用户界面中那些相等关系和不等关系。基于此,Cassowary 开发了一种规则系统,通过约束来描述视图间的关系。约束就是规则,这个规则能够表示出一个视图相对于另一个视图的位置。
由于 Cassowary 算法让视图位置可以按照一种简单的布局思路来写,这些简单的相对位置描述可以在运行时动态地计算出视图具体的位置。视图位置的写法简化了,界面相关代码也就更易于维护。苹果公司也是看重了这一点,将其引入到了自己的系统中。
2.生命周期
Auto Layout 不只有布局算法 Cassowary,还包含了布局在运行时的生命周期等一整套布局引擎系统,用来统一管理布局的创建、更新和销毁。了解 Auto Layout 的生命周期,是理解它的性能相关话题的基础。这样,在遇到问题,特别是性能问题时,我们才能从根儿上找到原因,从而避免或改进类似的问题。
这一整套布局引擎系统叫作 Layout Engine ,是 Auto Layout 的核心,主导着整个界面布局。
每个视图在得到自己的布局之前,Layout Engine 会将视图、约束、优先级、固定大小通过计算转换成最终的大小和位置。在 Layout Engine 里,每当约束发生变化,就会触发 Deffered Layout Pass,完成后进入监听约束变化的状态。当再次监听到约束变化,即进入下一轮循环中。整个过程如下图所示:
图中, Constraints Change 表示的就是约束变化,添加、删除视图时会触发约束变化。Activating或Deactivating,设置 Constant 或 Priority 时也会触发约束变化。Layout Engine 在碰到约束变化后会重新计算布局,获取到布局后调用 superview.setNeedLayout(),然后进入 Deferred Layout Pass。
Deferred Layout Pass 的主要作用是做容错处理。如果有些视图在更新约束时没有确定或缺失布局声明的话,会先在这里做容错处理。接下来,Layout Engine 会从上到下调用 layoutSubviews() ,通过 Cassowary 算法计算各个子视图的位置,算出来后将子视图的 frame 从 Layout Engine 里拷贝出来。
在这之后的处理,就和手写布局的绘制、渲染过程一样了。
3. Render Loop
Constraints Change属于Render Loop的一部分。渲染循环是一个每秒钟跑120次的进程。从而确保所有的内容能出现在每一帧上。它包含3个阶段:
- 更新约束 在
view的updateConstraints方法里,调用super.updateConstraints(), 层次向上传递更新约束,直到window - 布局 然后每个
view都会从上而下收到layoutsubviews的方法调用 - 显示 最后,每个view从上而下的被渲染到屏幕上。
三个过程对应的平行方法:
那么,渲染循环有什么好处呢,或者为什么要有渲染循环呢?答案就是避免工作的浪费。举个例子: 比如一个UILabel,需要用约束来表述其文本的尺寸,但是有很多属性都与文本尺寸有关,比如有文本本身属性,还有字体,文本大小等等。一种方法是,每当有属性发生更改时,就重新计算文本尺寸,但这往往是低效的,因为有时候我们会同时更改多个属性值。如果你是第一次设置一个UILabel, 你可能会设置一系列的属性,如果每次都重新测量文本的大小,那么中间的计算过程就被浪费掉了,因我们我们只想好最终的测量结果。这就是渲染循环为我们做得。我们只需要设置完想要的属性,在设置后更新约束,Render Loop会自动帮你算出文本尺寸,并完成渲染,避免多次重复的工作。
4.性能
先来看以一张图
上图是 WWDC 2018 中 里讲到的
Auto Layout 在 iOS 12 中优化后的表现。可以看到,优化后的性能,已经基本和手写布局一样可以达到性能随着视图嵌套的数量呈线性增长了。而在此之前的 Auto Layout,视图嵌套的数量对性能的影响是呈指数级增长的。
所以,你说 Auto Layout 对性能影响能大不大呢。但是,这个锅应该由 Cassowary 算法来背吗?在 1997 年时,Cassowary 是以高效的界面线性方程求解算法被提出来的。它解决的是界面的线性规划问题,而线性规划问题的解法是 Simplex 算法。单从 Simplex 算法的复杂度来看,多数情况下是没有指数时间复杂度的。而 Cassowary 算法又是在 Simplex 算法基础上对界面关系方程进行了高效的添加、修改更新操作,不会带来时间复杂度呈指数级增长的问题。
那么,如果 Cassowary 算法本身没有问题的话,问题就只可能是苹果公司在 iOS 12 之前在某些情况下没有用好这个算法。
接下来,我们再看一下 WWDC 2018 中 202 Session 的 Auto Layout 在兄弟视图独立开布局的情况。
可以看到,兄弟视图之间没有关系时,是不会出现性能呈指数增加问题的。这就表示 Cassowary 算法在添加时是高效的。但如果兄弟视图间有关系的话,在视图遍历时会不断处理和兄弟视图间的关系,这时会有修改更新计算。
由此可以看出,Auto Layout 并没有用上 Cassowary 高效修改更新的特性。
实际情况是,iOS 12 之前,很多约束变化时都会重新创建一个计算引擎 NSISEnginer 将约束关系重新加进来,然后重新计算。结果就是,涉及到的约束关系变多时,新的计算引擎需要重新计算,最终导致计算量呈指数级增加。
总结:OS12 的 Auto Layout 更多地利用了 Cassowary 算法的界面更新策略,使其真正完成了高效的界面线性策略计算。所以,可以在开发中放心大胆的使用Auto Layout了。