苹果在WWDC 2013上介绍了drawViewHierarchyInRect:
这个新的api,用于将指定view的整个view hierarchy 渲染在context中.renderInContext:
是运行在app本身的地址空间里的,使用的是CPU,而drawViewHierarchyInRect:
则是运行在app的地址空间之外的,使用了尽可能多的的GPU加速.
参考:http://stackoverflow.com/questions/23157653/drawviewhierarchyinrectafterscreenupdates-delays-other-animations
可以看到,在渲染复杂的包含多个UIImageView的视图时,新方法比renderInContext:
的速度的确快了不少.
试水
在Drlu中,一个主要的功能就是录制屏幕视频.之前使用的方法就是renderInContext:
.先用Time Profiler测试一下其运行情况:
1 | var width : size_t = CGBitmapContextGetWidth(self.screenshotContext) |
不看不知道,一看吓一跳.录制功能一共运行了十秒,期间CPU使用率一直在99%左右波动.renderInContext
占用了39.2%的运行时间.乖乖,原来这个App对CPU要求这么的高.
.ヾ (o ° ω ° O ) ノ゙
看看使用新方法的情况如何:
1 | autoreleasepool { |
同样是录制了十秒,cpu使用率在90%作用波动,而drawViewHierarchyInRect:
方法占用了24.8%的运行时间.
可能是因为被渲染的view没有太复杂的结构,所以两个方法的效果差别不是很大.
内存问题
在调用新方法时,我使用了autoreleasepool.原因是在如果不把这段代码放autoreleasepool中,录制时占用的内存会疯涨.观察代码,在每次调用此方法的时候,都需要新建一个context,在调用完这个方法后,虽然调用了UIGraphicsEndImageContext()
,但是由于这段代码外部是一个循环,所以没用的context没有被系统及时释放,而是需要等到录制完成,循环结束的时候才会释放.把这段代码放入autoreleasepool,可以强制每次截图完毕后立刻释放context所占用的空间.
发现新问题
注意到,在上面的图片中,getTimeOfDay
方法的占用的cpu时间占到了百分之32,而这个方法的作用是计算时间,当上一帧录制的时间到现在的时间超过1/25秒的时候调用截屏方法.这个方法要一直循环调用,并进行大量的计算.有没有其他方法呢.注意到CADisplayLink
可以在屏幕每刷新一次的时候调用指定方法,一般为每秒60次,利用标记变量,很容易使得每秒30次调用截屏方法.30帧和25帧相差不大,事不宜迟,试试新方法.
1 | //开始录制的时候调用------------------- |
在利用了CADisPlayLink
之后,cpu占用率直接从90%掉到了不足50%,性能上的提高是巨大的.原来,这个小小的计算时间的方法才是拖累性能的关键啊!Time Profiler
真是个好东西,帮我找出了拖累性能的罪魁祸首.
CADisplayLink和NSTimer
如果使用NSTimer并且设置60赫兹的频率,CADisplayLink和NSTimer的不同之处是CADisplayLink精确在每一帧展示时调用,而NSTimer的调用位置取决于创建NSTimer时的时间,但是只要不掉帧,其实都可以保证以每两帧为间隔取得后一帧的截图.所以其实使用CADisplayLink和NSTimer的区别是不大的.