在平时开发中,圆角处理是一个非常常见的场景,光栅化是一个最简单的优化方法,但是你所使用的方法真的如你所愿的在生效吗?这里对几种比较常用的方法对比一下。
首先按照最常用的几种方式设计了几个case,使用CollectionView来测试性能。Github
1. 纯粹的设置layer属性方式
1 | self.layer.cornerRadius = frame.size.width/2; |
这里不考虑非clipsToBounds的情况,因为大部分场景是需要的,而且非clipsToBounds的话和其他case并不平等。
2. 增加光栅化处理
1 | self.layer.shouldRasterize = YES; |
3. 绘制Image
1 | UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale); |
4. 绘制Image并且加上缓存
测试
以上case都是在init中就设置好了属性,不会产生reuse的性能问题。为了让效果更加明显,找了一个5s的设备进行测试。
下面是只有一个空的view的结果
从结果来看,似乎有点出乎意料,单纯的设置layer属性居然是表现最好的一个,而增加光栅化的表现明显最差,除去可能产生的误差,1和2的结果相差还是有点大。
打开模拟器的Offscreen-Rendered功能,发现1由于没有子视图,所以clipsToBounds属性无效,并不会发生离线渲染,而一旦指定了光栅化,就会强制执行离屏渲染,导致性能急剧下降。
那么我们在视图上加上一个视图
结果发生了戏剧性的变化。光栅化的视图直接接近0的帧率,而3和4除了初始化的时间有差别外其他基本符合预期,4高一点可能是用了同一份内存,拷贝到GPU的时候系统做了优化。
那么我们把label不要放在圆角view上
这时候1和2表现一致,应该都产生了离屏渲染。
所以问题来了
我们平时可能用到的光栅化为什么没用了。这要涉及到光栅化cache的策略了,我没有找到详细的官方文档,只找到一段话:
1 | And if you start setting lots of views with shouldRasterize, you're going to overflow the cache and that ends up in a really, really bad situation, ends up being much worse than before because essentially you're rendering every single view that you set with shouldRasterize offscreen and the back on the screen and we just talked about how doing that in every frame can really, really hurt your animation performance. |
大量的光栅化导致缓存溢出,所以每次还是需要离屏渲染。
另外,该文章还说
1 | So make sure you don't change anything during your view hierarchy while you have shouldRasterize on, otherwise you ended up rendering offscreen without great performance. |
这就是我们平时说的“不能经常变动的视图”,但是注意的是,应该是整个视图树,而且是anything,这样的要求太过于严格。所以为什么很多时候的光栅化会让性能问题更加严重。
HOW
最好的方式当然是绘制圆角并缓存。
另一种折中方案是使用blend,通过一张有透明通道的图片来遮盖实现圆角效果。
当你觉得你需要的时候,才开始优化。
这才是最好的思路,不要做过早的优化。