iOS核心動(dòng)畫(huà)高級(jí)技巧--(十五)圖層性能

在第14章『圖像IO』討論如何高效地載入和顯示圖像谴供,通過(guò)視圖來(lái)避免可能引起 動(dòng)畫(huà)幀率下降的性能問(wèn)題。在最后一章,我們將著重圖層樹(shù)本身数焊,以發(fā)掘最好的性 能。

隱式繪制

寄宿圖可以通過(guò)Core Graphics直接繪制佩耳,也可以直接載入一個(gè)圖片文件并賦值給contents屬性,或事先繪制一個(gè)屏幕之外的 CGContext上下文干厚。在之前的兩 章中我們討論了這些場(chǎng)景下的優(yōu)化。但是除了常見(jiàn)的顯式創(chuàng)建寄宿圖蛮瞄,你也可以通過(guò)以下三種方式創(chuàng)建隱式的:1,使用特性的圖層屬性挂捅。2芹助,特定的視圖。3闲先,特定 的圖層子類(lèi)状土。

了解這個(gè)情況為什么發(fā)生何時(shí)發(fā)生是很重要的,它能夠讓你避免引入不必要的軟件繪制行為伺糠。

文本

CATextLayerUILabel 都是直接將文本繪制在圖層的寄宿圖中蒙谓。事實(shí)上這兩種方式用了完全不同的渲染方式:在iOS 6及之前, UILabelWebKitHTML渲染引擎來(lái)繪制文本训桶,而 CATextLayer 用的是Core Text.后者渲染更迅速累驮,所以 在所有需要繪制大量文本的情形下都優(yōu)先使用它吧。但是這兩種方法都用了軟件的 方式繪制渊迁,因此他們實(shí)際上要比硬件加速合成方式要慢慰照。

不論如何,盡可能地避免改變那些包含文本的視圖的frame琉朽,因?yàn)檫@樣做的話(huà)文本就需要重繪毒租。例如,如果你想在圖層的角落里顯示一段靜態(tài)的文本箱叁,但是這個(gè)圖 層經(jīng)常改動(dòng)墅垮,你就應(yīng)該把文本放在一個(gè)子圖層中。

光柵化

在第四章『視覺(jué)效果』中我們提到了 CALayershouldRasterize 屬性耕漱,它可以解決重疊透明圖層的混合失靈問(wèn)題算色。同樣在第12章『速度的曲調(diào)』中,它也是作為繪制復(fù)雜圖層樹(shù)結(jié)構(gòu)的優(yōu)化方法螟够。
啟用 shouldRasterize 屬性會(huì)將圖層繪制到一個(gè)屏幕之外的圖像灾梦。然后這個(gè)圖 像將會(huì)被緩存起來(lái)并繪制到實(shí)際圖層的 contents 和子圖層峡钓。如果有很多的子圖層 或者有復(fù)雜的效果應(yīng)用,這樣做就會(huì)比重繪所有事務(wù)的所有幀劃得來(lái)得多若河。但是光 柵化原始圖像需要時(shí)間能岩,而且還會(huì)消耗額外的內(nèi)存。

當(dāng)我們使用得當(dāng)時(shí)萧福,光柵化可以提供很大的性能優(yōu)勢(shì)(如你在第12章所見(jiàn))拉鹃,但 是一定要避免作用在內(nèi)容不斷變動(dòng)的圖層上,否則它緩存方面的好處就會(huì)消失鲫忍,而且會(huì)讓性能變的更糟膏燕。

為了檢測(cè)你是否正確地使用了光柵化方式,用Instrument查看一下Color Hits GreenMisses Red項(xiàng)目悟民,是否已光柵化圖像被頻繁地刷新(這樣就說(shuō)明圖層并不 是光柵化的好選擇,或則你無(wú)意間觸發(fā)了不必要的改變導(dǎo)致了重繪行為)逾雄。

離屏渲染

當(dāng)圖層屬性的混合體被指定為在未預(yù)合成之前不能直接在屏幕中繪制時(shí)鸦泳,屏幕外渲染就被喚起了做鹰。屏幕外渲染并不意味著軟件繪制鼎姐,但是它意味著圖層必須在被顯示之前在一個(gè)屏幕外上下文中被渲染(不論CPU還是GPU)。圖層的以下屬性將會(huì) 觸發(fā)屏幕外繪制:

  • 圓角(當(dāng)和 maskToBounds 一起使用時(shí))
  • 圖層蒙板
  • 陰影

屏幕外渲染和我們啟用光柵化時(shí)相似饭尝,除了它并沒(méi)有像光柵化圖層那么消耗大钥平,子圖層并沒(méi)有被影響到姊途,而且結(jié)果也沒(méi)有被緩存捷兰,所以不會(huì)有長(zhǎng)期的內(nèi)存占用。但是秘蛇,如果太多圖層在屏幕外渲染依然會(huì)影響到性能。

有時(shí)候我們可以把那些需要屏幕外繪制的圖層開(kāi)啟光柵化以作為一個(gè)優(yōu)化方式赁还,前提是這些圖層并不會(huì)被頻繁地重繪秽浇。

對(duì)于那些需要?jiǎng)赢?huà)而且要在屏幕外渲染的圖層來(lái)說(shuō),你可以用CAShapeLayer 审残, contentsCenter或者shadowPath 來(lái)獲得同樣的表現(xiàn)而且 較少地影響到性能搅轿。

CAShapeLayer

cornerRadiusmaskToBounds 獨(dú)立作用的時(shí)候都不會(huì)有太大的性能問(wèn)題富玷, 但是當(dāng)他倆結(jié)合在一起,就觸發(fā)了屏幕外渲染雀鹃。有時(shí)候你想顯示圓角并沿著圖層裁 切子圖層的時(shí)候黎茎,你可能會(huì)發(fā)現(xiàn)你并不需要沿著圓角裁切当悔,這個(gè)情況下用 CAShapeLayer 就可以避免這個(gè)問(wèn)題了。

你想要的只是圓角且沿著矩形邊界裁切嗅骄,同時(shí)還不希望引起性能問(wèn)題溺森。其實(shí)你可以用現(xiàn)成的UIBezierPath的構(gòu)造器 + bezierPathWithRoundedRect:cornerRadius:這樣做并不會(huì)比直接用cornerRadius更快,但是它避免了性能問(wèn)題宏多。

可伸縮圖片

另一個(gè)創(chuàng)建圓角矩形的方法就是用一個(gè)圓形內(nèi)容圖片并結(jié)合第二章『寄宿圖』提 到的 contensCenter 屬性去創(chuàng)建一個(gè)可伸縮圖片(見(jiàn)清單15.2).理論上來(lái)說(shuō)伸但,這 個(gè)應(yīng)該比用CAShapeLayer要快,因?yàn)橐粋€(gè)可拉伸圖片只需要18個(gè)三角形(一個(gè) 圖片是由一個(gè)3*3網(wǎng)格渲染而成)铛铁,然而,許多都需要渲染成一個(gè)順滑的曲線(xiàn)括眠。在 實(shí)際應(yīng)用上倍权,二者并沒(méi)有太大的區(qū)別。

使用可伸縮圖片的優(yōu)勢(shì)在于它可以繪制成任意邊框效果而不需要額外的性能消耗当船。舉個(gè)例子德频,可伸縮圖片甚至還可以顯示出矩形陰影的效果缩幸。

shadowPath

在第2章我們有提到 shadowPath 屬性。如果圖層是一個(gè)簡(jiǎn)單幾何圖形如矩形或 者圓角矩形(假設(shè)不包含任何透明部分或者子圖層)钞护,創(chuàng)建出一個(gè)對(duì)應(yīng)形狀的陰影 路徑就比較容易患亿,而且Core Animation繪制這個(gè)陰影也相當(dāng)簡(jiǎn)單押逼,避免了屏幕外的 圖層部分的預(yù)排版需求挑格。這對(duì)性能來(lái)說(shuō)很有幫助沾歪。

如果你的圖層是一個(gè)更復(fù)雜的圖形,生成正確的陰影路徑可能就比較難了挫望,這樣子的話(huà)你可以考慮用繪圖軟件預(yù)先生成一個(gè)陰影背景圖媳板。

混合和過(guò)度繪制

在第12章有提到泉哈,GPU每一幀可以繪制的像素有一個(gè)最大限制(就是所謂的fill rate),這個(gè)情況下可以輕易地繪制整個(gè)屏幕的所有像素奕纫。但是如果由于重疊圖層的關(guān)系需要不停地重繪同一區(qū)域的話(huà),掉幀就可能發(fā)生了隙笆。

GPU會(huì)放棄繪制那些完全被其他圖層遮擋的像素撑柔,但是要計(jì)算出一個(gè)圖層是否被 遮擋也是相當(dāng)復(fù)雜并且會(huì)消耗處理器資源仰冠。同樣,合并不同圖層的透明重疊像素 (即混合)消耗的資源也是相當(dāng)客觀的辆沦。所以為了加速處理進(jìn)程肢扯,不到必須時(shí)刻不 要使用透明圖層担锤。任何情況下,你應(yīng)該這樣做:

  • 給視圖的 屬性設(shè)置一個(gè)固定的铭腕,不透明的顏色
  • 設(shè)置opaque 屬性為YES

這樣做減少了混合行為(因?yàn)榫幾g器知道在圖層之后的東西都不會(huì)對(duì)最終的像素 顏色產(chǎn)生影響)并且計(jì)算得到了加速累舷,避免了過(guò)度繪制行為因?yàn)?code>Core Animation可以舍棄所有被完全遮蓋住的圖層夹孔,而不用每個(gè)像素都去計(jì)算一遍。

如果用到了圖像只怎,盡量避免透明除非非常必要怜俐。如果圖像要顯示在一個(gè)固定的背景顏色或是固定的背景圖之前佑菩,你沒(méi)必要相對(duì)前景移動(dòng)裁赠,你只需要預(yù)填充背景圖片就可以避免運(yùn)行時(shí)混色了佩捞。

如果是文本的話(huà)蕾哟,一個(gè)白色背景的 UILabel(或者其他顏色)會(huì)比透明背景要 更高效谭确。

最后,明智地使用 shouldRasterize屬性芬迄,可以將一個(gè)固定的圖層體系折疊成 單張圖片昂秃,這樣就不需要每一幀重新合成了,也就不會(huì)有因?yàn)樽訄D層之間的混合和過(guò)度繪制的性能問(wèn)題了算途。

減少圖層數(shù)量

初始化圖層嘴瓤,處理圖層莉钙,打包通過(guò)IPC發(fā)給渲染引擎,轉(zhuǎn)化成OpenGL幾何圖 形狞贱,這些是一個(gè)圖層的大致資源開(kāi)銷(xiāo)。事實(shí)上蝎毡,一次性能夠在屏幕上顯示的最大圖 層數(shù)量也是有限的。

確切的限制數(shù)量取決于iOS設(shè)備别垮,圖層類(lèi)型扎谎,圖層內(nèi)容和屬性等。但是總得說(shuō)來(lái) 可以容納上百或上千個(gè)胧奔,下面我們將演示即使圖層本身并沒(méi)有做什么也會(huì)遇到的性 能問(wèn)題。

裁切

在對(duì)圖層做任何優(yōu)化之前胳泉,你需要確定你不是在創(chuàng)建一些不可見(jiàn)的圖層扇商,圖層在以下幾種情況下回事不可見(jiàn)的:

  • 圖層在屏幕邊界之外,或是在父圖層邊界之外案铺。
  • 完全在一個(gè)不透明圖層之后控汉。
  • 完全透明

Core Animation非常擅長(zhǎng)處理對(duì)視覺(jué)效果無(wú)意義的圖層涤姊。但是經(jīng)常性地,你自己 的代碼會(huì)比Core Animation更早地想知道一個(gè)圖層是否是有用的壁酬。理想狀況下舆乔,在圖層對(duì)象在創(chuàng)建之前就想知道剂公,以避免創(chuàng)建和配置不必要圖層的額外工作。

對(duì)象回收

處理巨大數(shù)量的相似視圖或圖層時(shí)還有一個(gè)技巧就是回收他們颜武。對(duì)象回收在iOS 頗為常見(jiàn); UITableViewUICollectionView都有用到鳞上, MKMapView中的動(dòng)畫(huà)pin碼也有用到吊档,還有其他很多例子。
對(duì)象回收的基礎(chǔ)原則就是你需要?jiǎng)?chuàng)建一個(gè)相似對(duì)象池鬼贱。當(dāng)一個(gè)對(duì)象的指定實(shí)例(本例子中指的是圖層)結(jié)束了使命这难,你把它添加到對(duì)象池中。每次當(dāng)你需要一個(gè)實(shí)例時(shí)脐帝,你就從池中取出一個(gè)糖权。當(dāng)且僅當(dāng)池中為空時(shí)再創(chuàng)建一個(gè)新的星澳。

這樣做的好處在于避免了不斷創(chuàng)建和釋放對(duì)象(相當(dāng)消耗資源,因?yàn)樯婕暗絻?nèi)存的分配和銷(xiāo)毀)而且也不必給相似實(shí)例重復(fù)賦值腿堤。

Core Graphics繪制

當(dāng)排除掉對(duì)屏幕顯示沒(méi)有任何貢獻(xiàn)的圖層或者視圖之后如暖,長(zhǎng)遠(yuǎn)看來(lái)盒至,你可能仍然 需要減少圖層的數(shù)量。例如樱衷,如果你正在使用多個(gè)UILabel 或者UIImageView 實(shí)例去顯示固定內(nèi)容酒唉,你可以把他們?nèi)刻鎿Q成一個(gè)單獨(dú)的視 圖,然后用- drawRect: 方法繪制出那些復(fù)雜的視圖層級(jí)侄榴。

這個(gè)提議看上去并不合理因?yàn)榇蠹叶贾儡浖L制行為要比GPU合成要慢而且還 需要更多的內(nèi)存空間网沾,但是在因?yàn)閳D層數(shù)量而使得性能受限的情況下,軟件繪制很 可能提高性能呢,因?yàn)樗苊饬藞D層分配和操作問(wèn)題证薇。

你可以自己實(shí)驗(yàn)一下這個(gè)情況,它包含了性能和柵格化的權(quán)衡浑度,但是意味著你可 以從圖層樹(shù)上去掉子圖層(用shouldRasterize,與完全遮擋圖層相反)甩骏。

-renderInContext: 方法

Core Graphics去繪制一個(gè)靜態(tài)布局有時(shí)候會(huì)比用層級(jí)的 UIView 實(shí)例來(lái)得快饮笛,但是使用UIView實(shí)例要簡(jiǎn)單得多而且比用手寫(xiě)代碼寫(xiě)出相同效果要可靠得 多论熙,更邊說(shuō)Interface Builder來(lái)得直接明了。為了性能而舍棄這些便利實(shí)在是不應(yīng) 該脓诡。

幸好祝谚,你不必這樣,如果大量的視圖或者圖層真的關(guān)聯(lián)到了屏幕上將會(huì)是一個(gè)大問(wèn)題交惯。沒(méi)有與圖層樹(shù)相關(guān)聯(lián)的圖層不會(huì)被送到渲染引擎商玫,也沒(méi)有性能問(wèn)題(在他們被創(chuàng)建和配置之后)。

使用 CALayer-renderInContext:方法袭异,你可以將圖層及其子圖層快照進(jìn) 一個(gè)Core Graphics上下文然后得到一個(gè)圖片炬藤,它可以直接顯示在UIImageView中,或者作為另一個(gè)圖層的contents上真。不同于shouldRasterize —— 要求圖層與圖層樹(shù)相關(guān)聯(lián) —— 睡互,這個(gè)方法沒(méi)有持續(xù)的 性能消耗。

當(dāng)圖層內(nèi)容改變時(shí)就珠,刷新這張圖片的機(jī)會(huì)取決于你(不同于 shouldRasterize妻怎,它自動(dòng)地處理緩存和緩存驗(yàn)證),但是一旦圖片被生成匿辩, 相比于讓Core Animation處理一個(gè)復(fù)雜的圖層樹(shù)榛丢,你節(jié)省了相當(dāng)客觀的性能。

總結(jié)

本章學(xué)習(xí)了使用Core Animation圖層可能遇到的性能瓶頸睬辐,并討論了如何避免或 減小壓力溯饵。你學(xué)習(xí)了如何管理包含上千虛擬圖層的場(chǎng)景(事實(shí)上只創(chuàng)建了幾百 個(gè))锨用。同時(shí)也學(xué)習(xí)了一些有用的技巧,選擇性地選取光柵化或者繪制圖層內(nèi)容在合 適的時(shí)候重新分配給CPUGPU啄巧。這些就是我們要講的關(guān)于Core Animation的全部 了(至少可以等到蘋(píng)果發(fā)明什么新的玩意兒)掌栅。

iOS核心動(dòng)畫(huà)高級(jí)技巧--目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末澄耍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子齐莲,更是在濱河造成了極大的恐慌选酗,老刑警劉巖岳枷,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呜叫,死亡現(xiàn)場(chǎng)離奇詭異怀偷,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)饭于,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)果覆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人局待,你說(shuō)我怎么就攤上這事钳榨∨γ牛” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵饼齿,是天一觀的道長(zhǎng)缕溉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)证鸥,這世上最難降的妖魔是什么败玉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任运翼,我火速辦了婚禮,結(jié)果婚禮上矩欠,老公的妹妹穿的比我還像新娘。我一直安慰自己躺坟,他們只是感情好乳蓄,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布虚倒。 她就那樣靜靜地躺著,像睡著了一般菠剩。 火紅的嫁衣襯著肌膚如雪耻煤。 梳的紋絲不亂的頭發(fā)上哈蝇,一...
    開(kāi)封第一講書(shū)人閱讀 52,549評(píng)論 1 312
  • 那天买鸽,我揣著相機(jī)與錄音,去河邊找鬼妆艘。 笑死看幼,一個(gè)胖子當(dāng)著我的面吹牛诵姜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播暇赤,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鞋囊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼溜腐!你這毒婦竟也來(lái)了译株?” 一聲冷哼從身側(cè)響起歉糜,我...
    開(kāi)封第一講書(shū)人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤匪补,失蹤者是張志新(化名)和其女友劉穎烂翰,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棵里,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年殿怜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了曙砂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸠澈。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖际度,靈堂內(nèi)的尸體忽然破棺而出涵妥,到底是詐尸還是另有隱情乖菱,我是刑警寧澤,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布蓬网,位于F島的核電站窒所,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏帆锋。R本人自食惡果不足惜吵取,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窟坐。 院中可真熱鬧海渊,春花似錦绵疲、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)郁岩。三九已至,卻和暖如春如叼,著一層夾襖步出監(jiān)牢的瞬間笼恰,已是汗流浹背社证。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工宜肉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留崖飘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親廷支。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恋拍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

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