一、问题
如果继承UILabel实现自己的一个Label,并且在子类的DrawRect方法中留空,什么都不写,会发生什么?
代码如下:
VC
@interface ViewController () @property (nonatomic, strong) DrawTestLabel *dwLabel; @end @implementation ViewController - (DrawTestLabel *)dwLabel { if(!_dwLabel) { _dwLabel = [[DrawTestLabel alloc] init]; _dwLabel.textAlignment = NSTextAlignmentCenter; } return _dwLabel; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [[self view] setBackgroundColor:[UIColor greenColor]]; [[self dwLabel] setFrame:CGRectMake(0, 0, 200, 50)]; [[self view] addSubview:[self dwLabel]]; [[self dwLabel] setText:@"Hello"]; [[self dwLabel] setCenter:[self view].center]; } @end
Label
@implementation DrawTestLabel - (void)drawRect:(CGRect)rect { // Drawing code // [super drawRect:rect]; } @end
注释调调用父类的DrawRect方法之后,会发现屏幕上面什么都没有
打开Xcode调试之后,会发现存在一个透明的图层
二、问题分析
我们知道,UIView和Layer的关系,Layer负责画面渲染,UIVIew负责事件响应;
在CALayer中通过实现 CALayerDelegate来实现画面的绘制,通常一个layer的delegate是UIView自身。
CALayerDelegate 的方法 drawlayer:incontext方法在每次绘制的时候,会调用UIView的Drawrect方法,在UIView实现了DrawRect的方法下。
那么上面自己继承的UILbel,重写Drawrect方法之后,大致的逻辑如下
1、CALayer接受到系统的回调,开始渲染
2、系统发现layer(Label)实现了Drawrect方法,那么会创建一个透明的画布,大小是UILabel的发小,scale是屏幕的scale
3、通过delegate调用Drawrect方法,在这个画布之上绘制内容
4、新的UILabel通过TextKit(底层是CoreText)来绘制文字到这个画布之上,这一段是在CPU上完成的
5、绘制完成的bitmap ,再一次Runloop循环中,会通过系统统一提交给后台的renderserver
6、render server进程处理不同的图层,通过操作GPU进行叠加渲染操作
为什么Drawrect消耗比较高?
因为绘制在CPU上面操作
绘制完成的bitmap,会通过跨进城IPC传递给render server存在系统调用消耗
render 传递内存中的bitmap到GPU的缓存上面生成纹理 ,再次产生消耗
普通的UIView通过设置属性的方式,不涉及到传递大块内存的操作,这些类似背景色、透明度、都是通过renderserver来完成的,GPU操作效率会比较高。