美文网首页
DrawRect内存飙升

DrawRect内存飙升

作者: C_HPY | 来源:发表于2018-08-07 12:06 被阅读103次

从很多地方听到过一个概念,重写

- (void)drawRect:(CGRect)rect

方法会造成内存飙升的问题,实践出真知,试了才知道。
场景如下:
一个简易画板view,这里只为了测试重写drawRect会造成的内存问题,不考虑一些画板平移之类的操作。
先上代码,定义一个CpuDrawView类

#import "CpuDrawView.h"

@interface CpuDrawView()

@property (nonatomic, strong) NSMutableArray *paths;

@end

@implementation CpuDrawView
#pragma mark - init
- (instancetype)init
{
    self = [super init];
    if (self) {
        self.backgroundColor = [UIColor whiteColor];
    }
    return self;
}
#pragma mark - drawRect
- (void)drawRect:(CGRect)rect
{
    for (UIBezierPath *path in self.paths) {
        // 连接处样式
        [path setLineJoinStyle:kCGLineJoinRound];
        // 头尾样式
        [path setLineCapStyle:kCGLineCapRound];
        [path stroke];
    }
}


#pragma mark - touch
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:touch.view];
    
    UIBezierPath *path = [UIBezierPath bezierPath];path.lineWidth = 5;
    [path moveToPoint:point];
    [self.paths addObject:path];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:touch.view];
    [[self.paths lastObject] addLineToPoint:point];
    [self setNeedsDisplay];
}


#pragma getter
- (NSMutableArray *)paths
{
    if (!_paths) {
        _paths  = [NSMutableArray array];
    }
    return _paths;
}
@end

在VC中的使用

/**
 drawRect方式画板   cpu绘制
 */
- (IBAction)cpu_Draw:(id)sender {
    CpuDrawView *cpuDrawView = [[CpuDrawView alloc] init];
    cpuDrawView.frame = CGRectMake(0, -kScreenHeight, kScreenWidth * 5, 2 * kScreenHeight - 100);
    [self.view addSubview:cpuDrawView];
}

在注释掉drawRect方法之后,app启动并创建cpuDrawView的内存占用如下:

//- (void)drawRect:(CGRect)rect
//{
//    for (UIBezierPath *path in self.paths) {
//        // 连接处样式
//        [path setLineJoinStyle:kCGLineJoinRound];
//        // 头尾样式
//        [path setLineCapStyle:kCGLineCapRound];
//        [path stroke];
//    }
//}
image.png

在如下两种方式下内存占用如下图

- (void)drawRect:(CGRect)rect
{
//    for (UIBezierPath *path in self.paths) {
//        // 连接处样式
//        [path setLineJoinStyle:kCGLineJoinRound];
//        // 头尾样式
//        [path setLineCapStyle:kCGLineCapRound];
//        [path stroke];
//    }
}
//或者
- (void)drawRect:(CGRect)rect
{
    for (UIBezierPath *path in self.paths) {
        // 连接处样式
        [path setLineJoinStyle:kCGLineJoinRound];
        // 头尾样式
        [path setLineCapStyle:kCGLineCapRound];
        [path stroke];
    }
}
image.png
可以看出,只要重写了drawRect方法,在创建的view不是特别大的情况下,内存已经出现了飙升,这个已经印证了上面的结论。
具体原因有一个UIView->CALayer->content(寄宿图)的概念,详细分析见 内存恶鬼drawRect - 谈画图功能的内存优化
内存恶鬼中提到用CAShaperLayer矢量图去解决类似问题,CAShaperLayer不会通过bitmap的方式去进行绘制,而且也不会创建寄宿图。
本着眼见为实的态度,我又写了一个使用UIBezierPath和CAShaperLayer结合使用进行绘制的case,见代码:
#import "GpuDrawView.h"

@interface GpuDrawView()

@property (nonatomic, strong) NSMutableArray *paths;
// 预留做撤销使用
@property (nonatomic, strong) NSMutableArray *layers;

@end

@implementation GpuDrawView
#pragma mark - init
- (instancetype)init
{
    self = [super init];
    if (self) {
        self.backgroundColor = [UIColor whiteColor];
    }
    return self;
}
#pragma mark - touch
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    UIBezierPath *path = [UIBezierPath bezierPath];
    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineJoinRound;
    [path moveToPoint:point];
    [self.paths addObject:path];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    UIBezierPath *path = [self.paths lastObject];
    [path addLineToPoint:point];
    
    CAShapeLayer *layer = [CAShapeLayer layer];
    layer.lineWidth = 5;
    layer.strokeStart = 0;
    layer.strokeEnd = 1;
    layer.lineCap = kCALineCapRound;
    layer.lineJoin = kCALineJoinRound;
    layer.strokeColor = [UIColor redColor].CGColor;
    layer.fillColor = [UIColor clearColor].CGColor;
    [self.layers addObject:layer];
    layer.path = path.CGPath;
    [self.layer addSublayer:layer];
}

#pragma mark - getter
- (NSMutableArray *)paths
{
    if (!_paths) {
        _paths  = [NSMutableArray array];
    }
    return _paths;
}
- (NSMutableArray *)layers
{
    if (!_layers) {
        _layers = [NSMutableArray array];
    }
    return _layers;
}
@end

实际操作证明,使用CAShaperLayer方式进行绘制内存不会有明显变化。
你也可以下载本文的demo

相关文章

网友评论

      本文标题:DrawRect内存飙升

      本文链接:https://www.haomeiwen.com/subject/ycvovftx.html