没必要要的效率考虑每每是性能问题的万恶之源。 ——William Allan Wulfgit
在第12章『速度的曲率』咱们学习如何用Instruments来诊断Core Animation性能问题。在构建一个iOS app的时候会遇到不少潜在的性能陷阱,可是在本章咱们将着眼于有关绘制的性能问题。github
术语绘图一般在Core Animation的上下文中指代软件绘图(意即:不禁GPU协助的绘图)。在iOS中,软件绘图一般是由Core Graphics框架完成来完成。可是,在一些必要的状况下,相比Core Animation和OpenGL,Core Graphics要慢了很多。app
软件绘图不只效率低,还会消耗可观的内存。CALayer
只须要一些与本身相关的内存:只有它的寄宿图会消耗必定的内存空间。即便直接赋给contents
属性一张图片,也不须要增长额外的照片存储大小。若是相同的一张图片被多个图层做为contents
属性,那么他们将会共用同一块内存,而不是复制内存块。框架
可是一旦你实现了CALayerDelegate
协议中的-drawLayer:inContext:
方法或者UIView
中的-drawRect:
方法(其实就是前者的包装方法),图层就建立了一个绘制上下文,这个上下文须要的大小的内存可从这个算式得出:图层宽*图层高*4字节,宽高的单位均为像素。对于一个在Retina iPad上的全屏图层来讲,这个内存量就是 2048*1526*4字节,至关于12MB内存,图层每次重绘的时候都须要从新抹掉内存而后从新分配。性能
软件绘图的代价昂贵,除非绝对必要,你应该避免重绘你的视图。提升绘制性能的秘诀就在于尽可能避免去绘制。学习
咱们用Core Graphics来绘图的一个一般缘由就是只是用图片或是图层效果不能轻易地绘制出矢量图形。矢量绘图包含一下这些:this
任意多边形(不只仅是一个矩形)atom
斜线或曲线spa
文本code
渐变
举个例子,清单13.1 展现了一个基本的画线应用。这个应用将用户的触摸手势转换成一个UIBezierPath
上的点,而后绘制成视图。咱们在一个UIView
子类DrawingView
中实现了全部的绘制逻辑,这个状况下咱们没有用上view controller。可是若是你喜欢你能够在view controller中实现触摸事件处理。图13.1是代码运行结果。
清单13.1 用Core Graphics实现一个简单的绘图应用
#import "DrawingView.h"@interface DrawingView () @property (nonatomic, strong) UIBezierPath *path;@end@implementation DrawingView- (void)awakeFromNib { //create a mutable path self.path = [[UIBezierPath alloc] init]; self.path.lineJoinStyle = kCGLineJoinRound; self.path.lineCapStyle = kCGLineCapRound;  self.path.lineWidth = 5; }- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ //get the starting point CGPoint point = [[touches anyObject] locationInView:self]; //move the path drawing cursor to the starting point [self.path moveToPoint:point]; }- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ //get the current point CGPoint point = [[touches anyObject] locationInView:self]; //add a new line segment to our path [self.path addLineToPoint:point]; //redraw the view [self setNeedsDisplay]; }- (void)drawRect:(CGRect)rect { //draw path [[UIColor clearColor] setFill]; [[UIColor redColor] setStroke]; [self.path stroke]; }@end
图13.1 用Core Graphics作一个简单的『素描』
这样实现的问题在于,咱们画得越多,程序就会越慢。由于每次移动手指的时候都会重绘整个贝塞尔路径(UIBezierPath
),随着路径愈来愈复杂,每次重绘的工做就会增长,直接致使了帧数的降低。看来咱们须要一个更好的方法了。
Core Animation为这些图形类型的绘制提供了专门的类,并给他们提供硬件支持(第六章『专有图层』有详细提到)。CAShapeLayer
能够绘制多边形,直线和曲线。CATextLayer
能够绘制文本。CAGradientLayer
用来绘制渐变。这些整体上都比Core Graphics更快,同时他们也避免了创造一个寄宿图。
若是稍微将以前的代码变更一下,用CAShapeLayer
替代Core Graphics,性能就会获得提升(见清单13.2).虽然随着路径复杂性的增长,绘制性能依然会降低,可是只有当很是很是浮躁的绘制时才会感到明显的帧率差别。
清单13.2 用CAShapeLayer
从新实现绘图应用
#import "DrawingView.h"#import <QuartzCore/QuartzCore.h>@interface DrawingView () @property (nonatomic, strong) UIBezierPath *path;@end@implementation DrawingView+ (Class)layerClass { //this makes our view create a CAShapeLayer //instead of a CALayer for its backing layer return [CAShapeLayer class]; }- (void)awakeFromNib { //create a mutable path self.path = [[UIBezierPath alloc] init]; //configure the layer CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer; shapeLayer.strokeColor = [UIColor redColor].CGColor; shapeLayer.fillColor = [UIColor clearColor].CGColor; shapeLayer.lineJoin = kCALineJoinRound; shapeLayer.lineCap = kCALineCapRound; shapeLayer.lineWidth = 5; }- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ //get the starting point CGPoint point = [[touches anyObject] locationInView:self]; //move the path drawing cursor to the starting point [self.path moveToPoint:point]; }- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ //get the current point CGPoint point = [[touches anyObject] locationInView:self]; //add a new line segment to our path [self.path addLineToPoint:point]; //update the layer with a copy of the path ((CAShapeLayer *)self.layer).path = self.path.CGPath; }@end