MelonTeam 移动终端前沿技术的探索者

iOS 绘图性能比较

2017-11-30
louisysshen
ios

在iOS中绘制UI一般有两种方法:一种是通过UIImageView或者UILabel建立相关图片和文字对象,然后添加到对应的父View中作为其SubView来实现;另外一种方法是直接在UIView中通过Core Graphics框架进行绘制。在一般情况下,这两种方法的性能孰优孰劣呢?

[ 直接添加SubView实现UI绘制 ]

[ 通过Core graphics实现UI绘制 ]

毛主席说过,实践是检验真理的唯一标准,为了得到一个令人信服的答案,up主做了一个简单的测试,通过实验验证两种方法的性能。测试程序生成了几十到上千张数量不等的图片,并分别通过上述两种方式绘制在相同的UIView上,利用Instruments监控时间和内存的消耗,实验结果如下所示。为了叙述方便,在下文中两种方法分别被称为添加法和绘制法。

实验结果分析

图片平铺时

当图片的排列方式为不重叠的平铺在父View上时,两种方式的时间消耗如下图所示。

[ 图片平铺时的时间开销 ]

从时间开销上来看,两者差距不大。进一步分析两种方式时间开销的来源可以看到,两种方式的时间开销来源并不相同,

[ 添加法时间开销来源 ]

[ 绘制法时间开销来源 ]

对于添加法来说,主要的时间开销来自三部分:UIImage对象的初始化,UIimageView对象的初始化,以及将UIimageView添加到父View上展示的渲染时间。而对于绘制法来说,相比添加法省去了初始化UIimageView对象的时间,但是在渲染的性能上差了一些。原因是Core Graphics调用了CPU进行绘制,相对来说性能会有一定的损失。综合起来看,时间开销上两种方式的差距不大。

另一方面,从内存占用情况看,两者的差距比较明显。添加法的内存占用随着图片数量的增长上升很快,而绘制的的内存占用则与图片数量的关系不大。原因也很好解释,添加法的额外内存开销就是对UIImageView对象的初始化。当图片数量增多时,iOS在这里似乎并没有重用的机制(即便所有UIImageView对象对应的是同一张图片),导致内存占用显著上升。

[ 图片平铺时的内存开销 ]

图片重叠时

当图片的排列方式为层层重叠在父View上时,两种方式的性能与平铺时的情况有所不同。

[ 图片覆盖时的时间开销 ]

在图片以覆盖的方式展示时,添加法的性能与平铺时相比并没有太大的变化,但是绘制法的性能出现了严重的问题。

[ 图片重叠时绘制法时间开销来源 ]

从Time Profiler的分析结果来看,绘制法的时间开销主要来自UIimage的drawInRect方法的调用。这个方法有两个参数blendModealphaalpha是up主熟知的透明度。blendMode则是图片的混合模式,该属性的默认值为kCGBlendModeNormal,即前景图的颜色会覆盖背景图,有趣的是,即便在这种情况下,在图片渲染时似乎仍对混合后的图片颜色进行了计算,而非像alpha值一样在不透明时直接不做计算,采用顶层图片的透明度。推测这里可能是导致性能下降的关键原因。

图层持有动画时

当对应的父View持有动画时,两种方式会对动画的性能产生影响吗?从测试的结果看,在图片数量增多时,采用添加法会导致动画的帧数明显下降,而绘制法则不会对动画性能产生影响。

[ 动画性能比较 ]

显然,当图层数量较多时,动画的性能会受到明显的影响。绘制法的好处是把绘制后的结果合成为一个图层,从而提升了动画的性能。

总结

综合上面的分析,两种方法的优缺点就很明显了。添加法的优点是通过GPU的渲染使得渲染速度较快,缺点则是会生成较多图层,内存占用高,且会影响动画性能;绘制法的优点是只生成一个图层,占用内存少,动画性能好,缺点是CPU渲染,速度慢,且在图片重叠时性能很差。那么,有没有两全齐美的方法呢?答案是有的,在《iOS核心动画高级技巧中》有这样的一段话:

使用CALayer renderInContext方法,你可以将图层及其子图层快照进一个Core Graphics上下文然后得到一个图片,它可以直接显示在UIImageView中,或者作为另一个图层的contents。

具体来说。可以先通过添加法绘制图层,然后通过父View的renderInContext方法将所有图层合成为一张图片,这样就兼顾了两种方法的性能优势。

[ 两全齐美的方法 ]


说一说

目录