drawRect 內(nèi)存暴增原因,如何優(yōu)化繪圖

前言

  • 最近有朋友問,直接用UILabel和自己用drawRect畫UILabel,哪個(gè)性能好?為什么?哪個(gè)占用的內(nèi)存少?為什么?

  • 其實(shí)這種問題的本質(zhì)就是使用drawRect會(huì)帶來哪些問題,性能上當(dāng)然是UILabel好了。

drawRect

每當(dāng)我們需要繪圖的時(shí)候,重寫UIView的drawRect方法,在此方法中進(jìn)行繪圖操作,然后蘋果要求我們調(diào)用UIView類中的setNeedsDisplay方法,系統(tǒng)就會(huì)自動(dòng)調(diào)用drawRect。
UIView繼承于UIResponder,需要實(shí)現(xiàn)CALayerDelegate。此處我們不得不提出UIView和CALayer,每一個(gè)UIView 內(nèi)部都有一個(gè)CALayer,UIView 的frame 直接返回的是layer的frame,在內(nèi)容的繪制上其實(shí)是CALayer 操作的,所以繪圖的關(guān)鍵還是在CALayer 上。那么CALayer 是如何顯示在屏幕上的呢?

image.png

contents:對(duì)象提供的內(nèi)容層,通常是CGImageRef,但也可能是其他。(Mac OS X 10.6及以后支持NSImage對(duì)象)默認(rèn)值是零。
CGImageRef,它是一個(gè)指向CGImage結(jié)構(gòu)的指針。UIImage有一個(gè)CGImage屬性,它返回一個(gè)”CGImageRef”,如果你想把這個(gè)值直接賦值給CALayer的contents,那你將會(huì)得到一個(gè)編譯錯(cuò)誤。因?yàn)镃GImageRef并不是一個(gè)真正的Cocoa對(duì)象,而是一個(gè)Core Foundation類型。 盡管Core Foundation類型跟Cocoa對(duì)象在運(yùn)行時(shí)貌似很像(被稱作toll-free bridging),他們并不是類型兼容的,不過你可以通過bridged關(guān)鍵字轉(zhuǎn)換。
所以要為CALayer圖層設(shè)置寄宿圖片屬性的最終代碼:
layer.contents = (__bridge id)image.CGImage;

除了賦值之外,我們的繪圖操作即是對(duì)contents(寄宿圖)繪制。
所以,回到問題,直接用UILabel和自己用drawRect畫UILabel,哪個(gè)性能好?
直接用UILabel,drawRect 方法沒有默認(rèn)實(shí)現(xiàn),所以說,寄宿圖也就不需要了。
用drawRect繪圖,默認(rèn)實(shí)現(xiàn)CALayerDelegate協(xié)議

image.png

displayLayer:(CALayer *)layer; 可以通過此方法直接設(shè)置contents
drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
在此方法調(diào)用之前,CALayer需要?jiǎng)?chuàng)建一個(gè)空寄宿圖(有尺寸)和一個(gè)Core
Graphics 的CGContextRef(上下文),當(dāng)繪制結(jié)束后,Core Animation打包所有圖層和動(dòng)畫屬性,然后通過IPC(內(nèi)部處理通信)發(fā)送到渲染服務(wù)器進(jìn)行顯示,同時(shí)上下文會(huì)被不斷渲染到屏幕上,直到下次調(diào)用setNeedsDisplay。
所以每次重繪都需要抹掉內(nèi)存重新分配,空寄宿圖的產(chǎn)生就消耗了大量?jī)?nèi)存,這也就是drawRect 內(nèi)存暴增原因。

那么我們?nèi)绻仨毷褂胐rawRect,如何優(yōu)化繪圖呢?
Core Graphics框架 有強(qiáng)大的api,但是UIBezierPath 對(duì)paths進(jìn)一步封裝,使用更加簡(jiǎn)單,同時(shí)我們使用CAShapeLayer 才會(huì)使UIBezierPath發(fā)揮出更大的作用。
CAShapeLayer繼承自CALayer,可以使用CALayer的所有屬性值。

CAShapeLayer屬于CoreAnimation框架,其動(dòng)畫渲染直接提交到手機(jī)的GPU當(dāng)中,相較于view的drawRect方法使用CPU渲染而言(實(shí)現(xiàn)drawRect消耗性能跟CoreGraphics 這個(gè)框架是基于CPU沒有關(guān)系),GPU圖像處理工作更多在硬件層面,效率極高;

一個(gè) CAShapeLayer 不需要像普通 CALayer 一樣創(chuàng)建一個(gè)寄宿圖形,所以無論有多大,都不會(huì)占用太多的內(nèi)存。

所以,以后關(guān)于繪圖,我們可以嘗試使用CAShapeLayer + UIBezierPath。

參考:
內(nèi)存惡鬼drawRect - 談畫圖功能的內(nèi)存優(yōu)化

本文查閱多方資料整理,有誤地方請(qǐng)指出,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容