iOS 核心动画的高效绘图

高效绘图

不必要的效率考虑往往是性能问题的万恶之源。——William Allan Wulf

如果你依然在编程的世界里迷茫,不知道自己的未来规划,小编给大家推荐一个IOS高级交流群:458839238 里面可以与大神一起交流并走出迷茫。小白可进群免费领取学习资料,看看前辈们是如何在编程的世界里傲然前行!
群内提供数据结构与算法、底层进阶、swift、逆向、整合面试题等免费资料
附上一份收集的各大厂面试题(附答案) ! 群文件直接获取
各大厂面试题

在第12章『速度的曲率』我们学习如何用Instruments来诊断Core Animation性能问题。在构建一个iOS app的时候会遇到很多潜在的性能陷阱,但是在本章我们将着眼于有关 绘制
的性能问题。

软件绘图

术语 绘图
通常在Core Animation的上下文中指代软件绘图(意即:不由GPU协助的绘图)。在iOS中,软件绘图通常是由Core Graphics框架完成来完成。但是,在一些必要的情况下,相比Core Animation和OpenGL,Core Graphics要慢了不少。

软件绘图不仅效率低,还会消耗可观的内存。 CALayer
只需要一些与自己相关的内存:只有它的寄宿图会消耗一定的内存空间。即使直接赋给 contents
属性一张图片,也不需要增加额外的照片存储大小。如果相同的一张图片被多个图层作为 contents
属性,那么他们将会共用同一块内存,而不是复制内存块。

但是一旦你实现了 CALayerDelegate
协议中的 -drawLayer:inContext:
方法或者 UIView
中的 -drawRect:
方法(其实就是前者的包装方法),图层就创建了一个绘制上下文,这个上下文需要的大小的内存可从这个算式得出:图层宽 图层高
4字节,宽高的单位均为像素。对于一个在Retina iPad上的全屏图层来说,这个内存量就是 2048 1526
4字节,相当于12MB内存,图层每次重绘的时候都需要重新抹掉内存然后重新分配。
软件绘图的代价昂贵,除非绝对必要,你应该避免重绘你的视图。提高绘制性能的秘诀就在于尽量避免去绘制。

矢量图形

我们用Core Graphics来绘图的一个通常原因就是只是用图片或是图层效果不能轻易地绘制出矢量图形。矢量绘图包含一下这些:

  • 任意多边形(不仅仅是一个矩形)
  • 斜线或曲线
  • 文本
  • 渐变

举个例子,清单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