一、問題
如果繼承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操作效率會比較高。