iOS Core Animation - The Performance of a Lifetime
前言
iOS Core Animation 读书笔记(三)The Performance of a Lifetime
笔记
Tuning for Speed
CPU Vs GPU
- 将各个layer以及layer的动画渲染到屏幕上由系统来控制,这个控制器称为render server。
- 绘制的过程如下:
- Layout:这个阶段建立好你的view树和layer树,并给layer设定好各种属性(frame,background color等等)。
- Display:这个阶段在每个layer上面画他们的backing image。这个阶段可能会调用
-drawRect:
或者-drawLayer:inContext:
方法。 - Prepare:这个阶段Core Animation准备好要传递给render server的动画的相关参数。
- Commit:这个阶段Core Animation将所有的layer和动画属性一起打包,传递给render server,让它显示。
- render server计算每个layer每个属性以及动画过程的中间值,然后设置OpenGL的各个三角形属性等等
- 在屏幕上绘制可视化的三角形。
- 上述的6歌步骤,我们只能控制前两步。前5步由CPU执行,最后一步由GPU执行。
- GPU方面有几个影响绘制的性能的因素:
- Too much geometry:GPU其实一次可以handle百万个三角形,不太可能成为性能瓶颈。但是太多的layer,在渲染前,需要都传递给render server,这一点会影响CPU的速度,成为影响性能的一个原因。
- Too much overdraw:GPU有这有限的像素填充速率。半透明的layer互相堆叠,那么就得渲染多次在同一个像素点。
- Offscreen drawing:
- Too-large images:GPU的最大的texture size是2048x2048/4096x4096。所画的图片超过这,GPU在显示的时候,就得先预处理一遍,这样会减慢速度。
- CPU上的操作主要会影响的是动画的开始,他会延迟动画的开始。体现在下面几个方面:
- Layout calculations:如果layer太复杂的话,计算所有的layer的frame等属性会花较长的时间。这个在使用autolayout的view上面体现的更明显。
- Lazy view loading:iOS只会在view要显示在屏幕上的时候,才会去加载view。这个对内存以及启动时间都很有帮助。但是当一个view显示时需要大量预先计算时,会对响应有很大的影响。
- Core Graphics drawing:也就是自己实现的
-drawRect:
方法和-drawLayer:inContext:
方法会影响性能。 - Image decompression:PNG或是JPEG都是压缩过的图片。一张图片在显示在iOS屏幕之前,都要先解压缩到原来的大小。这个过程也会消耗CPU时间。
Efficient Drawing
- 尽量不使用
-drawRect;
方法。 - 可以使用CAShapeLayer等来代替
-drawRect;
方法。 - 如果非要使用该方法,在重新绘制的时候,不要重新绘制整个View,要绘制那些需要绘制的区域。使用
-setNeedsDisplayInRect:
等方法。 - 可以尝试异步绘图,如使用CATiledLayer以及drawsAsynchronously等等。
Image IO
Image的本地加载和网络请求都会造成延迟。
可以尝试异步加载。
PNG相比于JPEG,加载时间较长,但是解压的时间短得多。这时因为JPEG的解压算法相比于基于zip压缩的PNG更加复杂。
UIImage的
+imageNamed;
方法与其他的方法不同的是,它在加载完图片后就立马开始解压,但是其他的方法会在开始画图时才去解压图片。还有一个方法就是直接将image制定为UIImageView的image属性或是layer的contents属性,但是这样的话只能在主线程中执行。来看段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
//dequeue cell
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
...
//switch to background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//load image
NSInteger index = indexPath.row;
NSString *imagePath = self.imagePaths[index];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
//redraw image using device context
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, YES, 0);
[image drawInRect:imageView.bounds];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//set image on main thread, but only if index still matches up
dispatch_async(dispatch_get_main_queue(), ^{
if (index == imageView.tag) {
imageView.image = image;
}
});
});
return cell;
}+imageNamed:
方法还会缓存解压之后的图片,以供以后使用。无论你是否还有指针指向这个UIImage。
Layer Performance
- CATextLayer和UILabel都是使用software drawing,所以会比那些使用hardware-accelerated的慢。所以要尽量避免改变有很多text的CATextLayer或是UILabel。
- 将shouldRasterize属性设置成YES,他会先在屏幕外面画好layer的image。然后这个image会被cache,之后会被画在真正的layer的位置上面。多次反复被使用。切记:如果一个layer的contents经常变化,不要使用这个。
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.