雷達(dá)圖繪制引發(fā)內(nèi)存的思考

近段一個(gè)新的需求是要做一個(gè)雷達(dá)圖用來展示數(shù)據(jù)硕勿,下圖為兩種方式繪制的雷達(dá)圖以及內(nèi)存使用情況:

drawRect方式繪制的雷達(dá)圖.png
CAShapeLayer方式繪制的雷達(dá)圖.png

在調(diào)研過后 發(fā)現(xiàn)現(xiàn)在市面上有兩種做雷達(dá)圖的方式一種是通過 drawRect進(jìn)行繪制 另一種則是通過 CAShapeLayer 去繪制幌绍,那么既然有兩種繪制方式的話, 那么就來對(duì)比一下這種兩種繪制方式在內(nèi)存方面的的優(yōu)與劣,如上圖,drawRect 方式繪制相同的雷達(dá)圖占用的內(nèi)存比CAShapeLayer 整整大了8M概而。那么drawRect方法為什么消耗的內(nèi)存比CAShapeLayer 大呢?

drawRect

如果想要了解drawRect,那我們就需要擼一擼在iOS程序上圖形顯示的原理了。在iOS系統(tǒng)中所有顯示的視圖都是從基類UIView繼承而來的奸攻,同時(shí)UIView負(fù)責(zé)接收用戶交互。但是實(shí)際上你所看到的視圖內(nèi)容庇忌,包括圖形等舞箍,都是由UIView的一個(gè)實(shí)例圖層屬性來繪制和渲染的,那就是CALayer皆疹。

CALayer類的概念與UIView非常類似,它也具有樹形的層級(jí)關(guān)系占拍,并且可以包含圖片文本略就、背景色等捎迫。它與UIView最大的不同在于它不能響應(yīng)用戶交互,可以說它根本就不知道響應(yīng)鏈的存在表牢,它的API雖然提供了“某點(diǎn)是否在圖層范圍內(nèi)的方法”窄绒,但是它并不具有響應(yīng)的能力。

在每一個(gè)UIView實(shí)例當(dāng)中崔兴,都有一個(gè)默認(rèn)的支持圖層彰导,UIView負(fù)責(zé)創(chuàng)建并且管理這個(gè)圖層。實(shí)際上這個(gè)CALayer圖層才是真正用來在屏幕上顯示的敲茄,UIView僅僅是對(duì)它的一層封裝位谋,實(shí)現(xiàn)了CALayerdelegate,提供了處理事件交互的具體功能堰燎,還有動(dòng)畫底層方法的高級(jí)API掏父。可以說CALayerUIView的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)秆剪。

腦補(bǔ)了這么多赊淑,它與今天的主題drawRect有何關(guān)系呢?別著急仅讽,我們既然已經(jīng)確定CALayer才是最終顯示到屏幕上的陶缺,只要順藤摸瓜,即可分析清楚洁灵。CALayer其實(shí)也只是iOS當(dāng)中一個(gè)普通的類组哩,它也并不能直接渲染到屏幕上,因?yàn)槠聊簧夏闼吹降臇|西处渣,其實(shí)都是一張張圖片伶贰。而為什么我們能看到CALayer的內(nèi)容呢,是因?yàn)?code>CALayer內(nèi)部有一個(gè)contents屬性罐栈。contents默認(rèn)可以傳一個(gè)id類型的對(duì)象黍衙,但是只有你傳CGImage的時(shí)候,它才能夠正常顯示在屏幕上荠诬。所以最終我們的圖形渲染落點(diǎn)落在contents身上琅翻。如圖:

image

contents也被稱為寄宿圖,除了給它賦值CGImage之外柑贞,我們也可以直接對(duì)它進(jìn)行繪制方椎,繪制的方法正是這次問題的關(guān)鍵,通過繼承UIView并實(shí)現(xiàn) -drawRect:方法即可自定義繪制钧嘶。但是呢drawRect:方法沒有默認(rèn)的實(shí)現(xiàn)棠众,因?yàn)閷?duì)UIView來說,寄宿圖并不是必須的,UIView不關(guān)心繪制的內(nèi)容闸拿。如果UIView檢測(cè)到-drawRect:方法被調(diào)用了空盼,它就會(huì)為視圖分配一個(gè)寄宿圖,這個(gè)寄宿圖的像素尺寸等于視圖大小乘以contentsScale(這個(gè)屬性與屏幕分辨率有關(guān)新荤,我們的雷達(dá)圖在不同模擬器下呈現(xiàn)的內(nèi)存用量不同也是因?yàn)樗?的值揽趾。

那么回到我們的雷達(dá)圖上,當(dāng)雷達(dá)圖從屏幕上出現(xiàn)的時(shí)候苛骨,因?yàn)橹貙懥?code>-drawRect:方法篱瞎,-drawRect :就會(huì)自動(dòng)調(diào)用。生成一張寄宿圖后痒芝,方法里面的代碼利用Core Graphics去繪制n條線條俐筋,然后內(nèi)容就會(huì)緩存起來,等待下次你調(diào)用-setNeedsDisplay時(shí)就會(huì)在進(jìn)行更新吼野。

雷達(dá)視圖的-drawRect:方法的背后實(shí)際上都是底層的CALayer進(jìn)行了重繪和保存中間產(chǎn)生的圖片校哎,CALayerdelegate屬性默認(rèn)實(shí)現(xiàn)了CALayerDelegate協(xié)議,當(dāng)它需要內(nèi)容信息的時(shí)候會(huì)調(diào)用協(xié)議中的方法來拿瞳步。

當(dāng)雷達(dá)視圖刷新數(shù)據(jù)重繪時(shí)闷哆,因?yàn)樗闹С謭D層CALayer的代理就是雷達(dá)視圖本身,所以支持圖層會(huì)請(qǐng)求雷達(dá)視圖給它一個(gè)寄宿圖來顯示单起,它此刻會(huì)調(diào)用:

- (void)displayLayer:(CALayer *)layer;  

如果雷達(dá)視圖實(shí)現(xiàn)了這個(gè)方法抱怔,就可以拿到layer來直接設(shè)置contents寄宿圖,如果這個(gè)方法沒有實(shí)現(xiàn)嘀倒,支持圖層CALayer會(huì)嘗試調(diào)用:

- (void)displayLayer:(CALayer *)layer;  

這個(gè)方法調(diào)用之前屈留,CALayer創(chuàng)建了一個(gè)合適尺寸的空寄宿圖(尺寸由boundscontentsScale決定)和一個(gè)Core Graphics的繪制上下文環(huán)境,為繪制寄宿圖做準(zhǔn)備测蘑,它作為ctx參數(shù)傳入灌危。在這一步生成的空寄宿圖內(nèi)存是相當(dāng)巨大的,它就是本次內(nèi)存問題的關(guān)鍵碳胳,一旦你實(shí)現(xiàn)了CALayerDelegate協(xié)議中的-drawLayer:inContext:方法或者UIView中的-drawRect:方法(其實(shí)就是前者的包裝方法)勇蝙,圖層就創(chuàng)建了一個(gè)繪制上下文,這個(gè)上下文需要的內(nèi)存可從這個(gè)公式得出:圖層寬* 圖層高 * 4字節(jié)挨约,寬高的單位均為像素味混。 而既然我們的雷達(dá)圖是封裝的庫 就應(yīng)該適應(yīng)各個(gè)尺寸下內(nèi)存的要求,比如我們當(dāng)前的雷達(dá)圖尺寸下需要開辟的內(nèi)存空間大小為:

 radarV = [[JYRadarChart alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, 400)];  

那么我們?cè)?P機(jī)器 上下文的內(nèi)存量則是: 1242 * 1200 * 4字節(jié) 其實(shí)算下來大概有5兆多诫惭。在其內(nèi)部又有通過drawRect方法去實(shí)現(xiàn)繪制文字 title 則加起來有 8M多

CAShapeLayer

CAShapeLayer是一個(gè)通過矢量圖形而不是bitmap來繪制的圖層子類翁锡。用CGPath來定義想要繪制的圖形,CAShapeLayer會(huì)自動(dòng)渲染夕土。它可以完美替代我們的直接使用Core Graphics繪制layer馆衔,對(duì)比之下使用CAShapeLayer有以下優(yōu)點(diǎn)

  • 渲染快速。CAShapeLayer使用了硬件加速,繪制同一圖形會(huì)比用Core Graphics快很多哈踱。
  • 高效使用內(nèi)存荒适。一個(gè)CAShapeLayer不需要像普通CALayer一樣創(chuàng)建一個(gè)寄宿圖形梨熙,所以無論有多大开镣,都不會(huì)占用太多的內(nèi)存。
  • 不會(huì)被圖層邊界剪裁掉咽扇。一個(gè)CAShapeLayer可以在邊界之外繪制邪财。你的圖層路徑不會(huì)像在使用Core Graphics的普通CALayer一樣被剪裁掉
  • 不會(huì)出現(xiàn)像素化。當(dāng)你給CAShapeLayer做3D變換時(shí)质欲,它不像一個(gè)有寄宿圖的普通圖層一樣變得像素化

通過兩個(gè)demo的對(duì)比下 在當(dāng)前尺寸下確實(shí)內(nèi)存上會(huì)相差8M左右 树埠,如果其他設(shè)計(jì)要求雷達(dá)圖尺寸大一點(diǎn)的話 那么就會(huì)產(chǎn)生更大的內(nèi)存消耗。

在此總結(jié)一下繪制性能優(yōu)化原則:

  • 1.繪制圖形性能的優(yōu)化最好的辦法就是不去繪制嘶伟。
  • 2.利用專有圖層代替繪圖需求怎憋。
  • 3.不得不用到繪圖盡量縮小視圖面積,并且盡量降低重繪頻率九昧。
  • 4.異步繪制绊袋,推測(cè)內(nèi)容,提前在其他線程繪制圖片铸鹰,在主線程中直接設(shè)置圖片癌别。

當(dāng)然了,這兩種雷達(dá)圖的實(shí)現(xiàn)方式各有優(yōu)缺點(diǎn)蹋笼,也很感謝雷達(dá)圖作者開源展姐。
本人修改的兩種實(shí)現(xiàn)方式的雷達(dá)圖地址Demo,只用于粗略的對(duì)比剖毯。

參考鏈接:
iOS RadarChart
JYRadarChart
drawRect內(nèi)存惡鬼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末圾笨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子逊谋,更是在濱河造成了極大的恐慌擂达,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涣狗,死亡現(xiàn)場(chǎng)離奇詭異谍婉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)镀钓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門穗熬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丁溅,你說我怎么就攤上這事唤蔗。” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵妓柜,是天一觀的道長(zhǎng)箱季。 經(jīng)常有香客問我,道長(zhǎng)棍掐,這世上最難降的妖魔是什么藏雏? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮作煌,結(jié)果婚禮上掘殴,老公的妹妹穿的比我還像新娘。我一直安慰自己粟誓,他們只是感情好奏寨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹰服,像睡著了一般病瞳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悲酷,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天套菜,我揣著相機(jī)與錄音,去河邊找鬼舔涎。 笑死笼踩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亡嫌。 我是一名探鬼主播嚎于,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼挟冠!你這毒婦竟也來了于购?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤知染,失蹤者是張志新(化名)和其女友劉穎肋僧,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體控淡,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嫌吠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掺炭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辫诅。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖涧狮,靈堂內(nèi)的尸體忽然破棺而出炕矮,到底是詐尸還是另有隱情么夫,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布肤视,位于F島的核電站档痪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏邢滑。R本人自食惡果不足惜腐螟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望殊鞭。 院中可真熱鬧遭垛,春花似錦尼桶、人聲如沸操灿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽趾盐。三九已至,卻和暖如春小腊,著一層夾襖步出監(jiān)牢的瞬間救鲤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工秩冈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留本缠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓入问,卻偏偏與公主長(zhǎng)得像丹锹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芬失,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容