如何解決CALayer的contents內(nèi)存暴增問題

前兩天QQ群大佬提出一個問題雌澄,如何解決CALayer的contents屬性賦值之后的內(nèi)存暴增問題斋泄,百度了一下無頭緒,偶然看到了唐巧的文章镐牺,感覺很有意思炫掐,特此記錄一下。原文在此:內(nèi)存惡鬼drawRect - 談畫圖功能的內(nèi)存優(yōu)化

正文:

先來說一下contens是個啥東西:CoreAnimation:CALayer的contents
唐巧文章里寫的很清楚了睬涧,在這里不再過多的贅述募胃,大概是這樣的:

  1. 搞了一個簡易功能的畫板,記錄手指觸摸的軌跡然后繪制在屏幕上畦浓。
  2. 發(fā)現(xiàn)兩個問題:當畫板彈出痹束,其余無任何操作時,內(nèi)存激增讶请,然后當手指繪制開始時祷嘶,內(nèi)存又激增。
  3. 分析原因可能有兩個:一是在手指繪制的過程中創(chuàng)建的大量點對象沒有及時釋放或者其他資源沒有及時釋放夺溢。這一點因為工程為ARCInstruments工具得以排除论巍。二是系統(tǒng)在繪制過程中開始大量消耗內(nèi)存,但是明顯可以發(fā)現(xiàn)是畫板創(chuàng)建之后就會有內(nèi)存激增风响,所以矛頭直指drawRect环壤。
    這是畫板繪制功能的一段代碼:
- (void)drawRect:(CGRect)rect
{
    if (!self.paths.count) return;
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    for (BHBPaintPath *path in self.paths) {
      CGContextSaveGState(ctx);
      [[UIColor blackColor] set];
      [path stroke]; // 關(guān)鍵的一步繪制
      CGContextRestoreGState(ctx);
    }
}
  1. 分析為何drawRect會出現(xiàn)內(nèi)存暴增的真正原因:
    簡單來說,咱們所看到的屏幕上的東西钞诡,其實都是一張圖片,是由CALayercontents屬性掌管著湃崩,最終圖形渲染落點落在了contents身上荧降。
  2. contents也被稱為寄宿圖,除了給它賦值CGImage以外攒读,我們也可以直接對它進行繪制朵诫,繪制的方法正式此次問題的關(guān)鍵,通過集成UIView并實現(xiàn)-drawRect:方法即可自定義繪制薄扁。-drawRect:方法沒有默認的實現(xiàn)剪返,因此對UIView來說废累,寄宿圖并不是必須的,UIView不關(guān)心繪制的內(nèi)容脱盲。如果UIView檢測到-drawRect:方法被調(diào)用了邑滨,他就會為視圖分配一個寄宿圖,這個寄宿圖的像素尺寸等于視圖大小乘以contentsScale(這個屬性與屏幕分辨率有關(guān)钱反,我們的畫板程序在不同模擬器下呈現(xiàn)的內(nèi)存用量不同也是因為它)的值掖看。
    那么我們重回畫板程序,當畫板從屏幕上出現(xiàn)的時候面哥,因為重寫了-drawRect:方法哎壳,-drawRect:方法就會自動調(diào)用。生成一張寄宿圖后尚卫,方法里面的代碼利用Core Graphics去繪制n條黑色的線归榕,然后 內(nèi)容就會緩存起來,等待下次你調(diào)用-setNeedsDisplay時再進行更新吱涉。
    畫板視圖的-drawRect:方法的背后實際上都是底層的CALayer進行了重繪和保存中間產(chǎn)生的圖片刹泄,CALayerdelegate屬性默認實現(xiàn)了CALyaerDelegate協(xié)議,當它需要內(nèi)容信息的時候回調(diào)用協(xié)議中的方法來拿邑飒。當畫板重繪時循签,因為他的支持圖層CALayer的代理就是畫板視圖的本身,所以支持圖層會請求畫板視圖給它一個寄宿圖來顯示疙咸,它此刻會調(diào)用:
- (void)displayLayer:(CALayer *)layer;

如果畫板試圖實現(xiàn)了這個方法县匠,就可以拿到layer來直接設(shè)置contents寄宿圖,如果這個方法沒有實現(xiàn)撒轮,支持圖層CALayer會嘗試調(diào)用:

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;

這個方法調(diào)用之前乞旦,CALayer創(chuàng)建了一個合適尺寸的空寄宿圖(尺寸由boundscontentsScale決定)和一個Core Graphics的繪制山下文環(huán)境,為繪制寄宿圖做準備题山,它作為ctx參數(shù)傳入兰粉。在這一步生成的空寄宿圖內(nèi)存是相當巨大的,它就是本次內(nèi)存問題的關(guān)鍵顶瞳,一旦你實現(xiàn)了CALayerDelegate協(xié)議中的-drawLayer:inContext:方法或者UIView中的-drawRect:方法(其實就是前者的包裝方法)玖姑,圖層就創(chuàng)建了一個繪制上下文,這個上下文需要的內(nèi)存可從這個公式得出:圖層寬圖層高4字節(jié)慨菱,寬高的單位均為像素焰络。所以圖層在每次繪制的時候都需要重新抹掉內(nèi)存然后重新分配。它就是我們畫板程序內(nèi)存暴增的真正原因符喝。

  1. 解決方法
    處理類似于畫板這樣畫線條的需求直接用專用圖層CAShapeLayer闪彼。
    來看看這是個什么東西:
    CAShapeLayer是一個通過矢量圖形而不是bitmap來繪制的圖層子類。用CGPath來定義想要繪制的圖形协饲,CAShapeLayer會自動渲染畏腕。他可以完美替代我們直接使用Core Graphics繪制layer缴川,相比之下使用CAShapeLayer有以下優(yōu)點:
  • 渲染快速。CAShapeLayer使用了硬件加速描馅,繪制同一圖形會比用Core Graphics快很多把夸。
  • 高效使用內(nèi)存。一個CAShapeLayer不需要像普通CALayer一樣創(chuàng)建一個寄宿圖形流昏,所以無論有多大扎即,都不會占用太多的內(nèi)存。
  • 不會被圖形邊界剪裁掉况凉。
  • 不會出現(xiàn)像素化谚鄙。
  1. 總結(jié)一下繪制性能優(yōu)化原則:
  • 繪制圖形性能的優(yōu)化最好的辦法就是不去繪制。
  • 利用專有圖層代替繪圖需求刁绒。
  • 不得不用到的繪圖盡量縮小試圖面積闷营,并且盡量降低重繪頻率。
  • 異步繪制知市,推測內(nèi)容傻盟,提前在其他線程繪制圖片,在主線程中直接設(shè)置圖片嫂丙。
  1. 最后附上畫板-demo
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娘赴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子跟啤,更是在濱河造成了極大的恐慌诽表,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隅肥,死亡現(xiàn)場離奇詭異竿奏,居然都是意外死亡腥放,警方通過查閱死者的電腦和手機泛啸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秃症,“玉大人泌神,你說我怎么就攤上這事良漱∥枋” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵母市,是天一觀的道長矾兜。 經(jīng)常有香客問我,道長患久,這世上最難降的妖魔是什么椅寺? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蒋失,結(jié)果婚禮上返帕,老公的妹妹穿的比我還像新娘。我一直安慰自己篙挽,他們只是感情好荆萤,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铣卡,像睡著了一般链韭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上煮落,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天敞峭,我揣著相機與錄音,去河邊找鬼蝉仇。 笑死旋讹,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的量淌。 我是一名探鬼主播骗村,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼呀枢!你這毒婦竟也來了胚股?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤裙秋,失蹤者是張志新(化名)和其女友劉穎琅拌,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摘刑,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡进宝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了枷恕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片党晋。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出未玻,到底是詐尸還是另有隱情灾而,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布扳剿,位于F島的核電站旁趟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏庇绽。R本人自食惡果不足惜锡搜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瞧掺。 院中可真熱鬧耕餐,春花似錦、人聲如沸夸盟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽上陕。三九已至桩砰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間释簿,已是汗流浹背亚隅。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留庶溶,地道東北人煮纵。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像偏螺,于是被迫代替她去往敵國和親行疏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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