從很多地方聽到過一個概念萌抵,重寫
- (void)drawRect:(CGRect)rect
方法會造成內(nèi)存飆升的問題稿饰,實(shí)踐出真知缰儿,試了才知道畦粮。
場景如下:
一個簡易畫板view,這里只為了測試重寫drawRect會造成的內(nèi)存問題乖阵,不考慮一些畫板平移之類的操作宣赔。
先上代碼,定義一個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啟動并創(chuàng)建cpuDrawView的內(nèi)存占用如下:
//- (void)drawRect:(CGRect)rect
//{
// for (UIBezierPath *path in self.paths) {
// // 連接處樣式
// [path setLineJoinStyle:kCGLineJoinRound];
// // 頭尾樣式
// [path setLineCapStyle:kCGLineCapRound];
// [path stroke];
// }
//}
image.png
在如下兩種方式下內(nèi)存占用如下圖
- (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方法儒将,在創(chuàng)建的view不是特別大的情況下,內(nèi)存已經(jīng)出現(xiàn)了飆升对蒲,這個已經(jīng)印證了上面的結(jié)論钩蚊。
具體原因有一個UIView->CALayer->content(寄宿圖)的概念贡翘,詳細(xì)分析見 內(nèi)存惡鬼drawRect - 談畫圖功能的內(nèi)存優(yōu)化
內(nèi)存惡鬼中提到用CAShaperLayer矢量圖去解決類似問題,CAShaperLayer不會通過bitmap的方式去進(jìn)行繪制砰逻,而且也不會創(chuàng)建寄宿圖鸣驱。
本著眼見為實(shí)的態(tài)度,我又寫了一個使用UIBezierPath和CAShaperLayer結(jié)合使用進(jìn)行繪制的case蝠咆,見代碼:
#import "GpuDrawView.h"
@interface GpuDrawView()
@property (nonatomic, strong) NSMutableArray *paths;
// 預(yù)留做撤銷使用
@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
實(shí)際操作證明踊东,使用CAShaperLayer方式進(jìn)行繪制內(nèi)存不會有明顯變化。
你也可以下載本文的demo