本文轉載自:http://www.cocoachina.com/ios/20150106/10839.html? 為了防止cocochina以后刪除該文章系奉,故轉載至此秧秉;
性能調優(yōu)
代碼應該運行的盡量快阔挠,而不是更快 - 理查德
在第一和第二部分,我們了解了Core Animation提供的關于繪制和動畫的一些特性。Core Animation功能和性能都非常強大,但如果你對背后的原理不清楚的話也會降低效率驹止。讓它達到最優(yōu)的狀態(tài)是一門藝術。在這章中观蜗,我們將探究一些動畫運行慢的原因臊恋,以及如何去修復這些問題。
CPU VS GPU
關于繪圖和動畫有兩種處理的方式:CPU(中央處理器)和GPU(圖形處理器)墓捻。在現(xiàn)代iOS設備中抖仅,都有可以運行不同軟件的可編程芯片,但是由于歷史原因,我們可以說CPU所做的工作都在軟件層面撤卢,而GPU在硬件層面环凿。
總的來說,我們可以用軟件(使用CPU)做任何事情放吩,但是對于圖像處理智听,通常用硬件會更快,因為GPU使用圖像對高度并行浮點運算做了優(yōu)化渡紫。由于某些原因瞭稼,我們想盡可能把屏幕渲染的工作交給硬件去處理。問題在于GPU并沒有無限制處理性能腻惠,而且一旦資源用完的話环肘,性能就會開始下降了(即使CPU并沒有完全占用)
大多數(shù)動畫性能優(yōu)化都是關于智能利用GPU和CPU,使得它們都不會超出負荷集灌。于是我們首先需要知道Core Animation是如何在這兩個處理器之間分配工作的悔雹。
動畫的舞臺
Core Animation處在iOS的核心地位:應用內(nèi)和應用間都會用到它。一個簡單的動畫可能同步顯示多個app的內(nèi)容欣喧,例如當在iPad上多個程序之間使用手勢切換腌零,會使得多個程序同時顯示在屏幕上。在一個特定的應用中用代碼實現(xiàn)它是沒有意義的唆阿,因為在iOS中不可能實現(xiàn)這種效果(App都是被沙箱管理益涧,不能訪問別的視圖)。
動畫和屏幕上組合的圖層實際上被一個單獨的進程管理驯鳖,而不是你的應用程序闲询。這個進程就是所謂的渲染服務。在iOS5和之前的版本是SpringBoard進程(同時管理著iOS的主屏)浅辙。在iOS6之后的版本中叫做BackBoard扭弧。
當運行一段動畫時候,這個過程會被四個分離的階段被打破:
布局 - 這是準備你的視圖/圖層的層級關系记舆,以及設置圖層屬性(位置鸽捻,背景色,邊框等等)的階段泽腮。
顯示 - 這是圖層的寄宿圖片被繪制的階段御蒲。繪制有可能涉及你的-drawRect:和-drawLayer:inContext:方法的調用路徑。
準備 - 這是Core Animation準備發(fā)送動畫數(shù)據(jù)到渲染服務的階段诊赊。這同時也是Core Animation將要執(zhí)行一些別的事務例如解碼動畫過程中將要顯示的圖片的時間點厚满。
提交 - 這是最后的階段,Core Animation打包所有圖層和動畫屬性豪筝,然后通過IPC(內(nèi)部處理通信)發(fā)送到渲染服務進行顯示痰滋。
但是這些僅僅階段僅僅發(fā)生在你的應用程序之內(nèi)摘能,在動畫在屏幕上顯示之前仍然有更多的工作续崖。一旦打包的圖層和動畫到達渲染服務進程敲街,他們會被反序列化來形成另一個叫做渲染樹的圖層樹(在第一章“圖層樹”中提到過)。使用這個樹狀結構严望,渲染服務對動畫的每一幀做出如下工作:
對所有的圖層屬性計算中間值多艇,設置OpenGL幾何形狀(紋理化的三角形)來執(zhí)行渲染
在屏幕上渲染可見的三角形
所以一共有六個階段;最后兩個階段在動畫過程中不停地重復像吻。前五個階段都在軟件層面處理(通過CPU)峻黍,只有最后一個被GPU執(zhí)行。而且拨匆,你真正只能控制前兩個階段:布局和顯示姆涩。Core Animation框架在內(nèi)部處理剩下的事務,你也控制不了它惭每。
這并不是個問題骨饿,因為在布局和顯示階段,你可以決定哪些由CPU執(zhí)行台腥,哪些交給GPU去做宏赘。那么改如何判斷呢?
GPU相關的操作
GPU為一個具體的任務做了優(yōu)化:它用來采集圖片和形狀(三角形)黎侈,運行變換察署,應用紋理和混合然后把它們輸送到屏幕上。現(xiàn)代iOS設備上可編程的GPU在這些操作的執(zhí)行上又很大的靈活性峻汉,但是Core Animation并沒有暴露出直接的接口贴汪。除非你想繞開Core Animation并編寫你自己的OpenGL著色器,從根本上解決硬件加速的問題休吠,那么剩下的所有都還是需要在CPU的軟件層面上完成嘶是。
寬泛的說,大多數(shù)CALayer的屬性都是用GPU來繪制蛛碌。比如如果你設置圖層背景或者邊框的顏色聂喇,那么這些可以通過著色的三角板實時繪制出來。如果對一個contents屬性設置一張圖片蔚携,然后裁剪它 - 它就會被紋理的三角形繪制出來希太,而不需要軟件層面做任何繪制。
但是有一些事情會降低(基于GPU)圖層繪制酝蜒,比如:
太多的幾何結構 - 這發(fā)生在需要太多的三角板來做變換誊辉,以應對處理器的柵格化的時候。現(xiàn)代iOS設備的圖形芯片可以處理幾百萬個三角板亡脑,所以在Core Animation中幾何結構并不是GPU的瓶頸所在堕澄。但由于圖層在顯示之前通過IPC發(fā)送到渲染服務器的時候(圖層實際上是由很多小物體組成的特別重量級的對象)邀跃,太多的圖層就會引起CPU的瓶頸。這就限制了一次展示的圖層個數(shù)(見本章后續(xù)“CPU相關操作”)蛙紫。
重繪 - 主要由重疊的半透明圖層引起拍屑。GPU的填充比率(用顏色填充像素的比率)是有限的,所以需要避免重繪(每一幀用相同的像素填充多次)的發(fā)生坑傅。在現(xiàn)代iOS設備上僵驰,GPU都會應對重繪;即使是iPhone 3GS都可以處理高達2.5的重繪比率唁毒,并任然保持60幀率的渲染(這意味著你可以繪制一個半的整屏的冗余信息蒜茴,而不影響性能),并且新設備可以處理更多浆西。
離屏繪制 - 這發(fā)生在當不能直接在屏幕上繪制粉私,并且必須繪制到離屏圖片的上下文中的時候。離屏繪制發(fā)生在基于CPU或者是GPU的渲染近零,或者是為離屏圖片分配額外內(nèi)存诺核,以及切換繪制上下文,這些都會降低GPU性能秒赤。對于特定圖層效果的使用猪瞬,比如圓角,圖層遮罩入篮,陰影或者是圖層光柵化都會強制Core Animation提前渲染圖層的離屏繪制陈瘦。但這不意味著你需要避免使用這些效果,只是要明白這會帶來性能的負面影響潮售。
過大的圖片 - 如果視圖繪制超出GPU支持的2048x2048或者4096x4096尺寸的紋理痊项,就必須要用CPU在圖層每次顯示之前對圖片預處理,同樣也會降低性能酥诽。
CPU相關的操作
大多數(shù)工作在Core Animation的CPU都發(fā)生在動畫開始之前鞍泉。這意味著它不會影響到幀率,所以很好肮帐,但是他會延遲動畫開始的時間咖驮,讓你的界面看起來會比較遲鈍。
以下CPU的操作都會延遲動畫的開始時間:
布局計算 - 如果你的視圖層級過于復雜训枢,當視圖呈現(xiàn)或者修改的時候托修,計算圖層幀率就會消耗一部分時間。特別是使用iOS6的自動布局機制尤為明顯恒界,它應該是比老版的自動調整邏輯加強了CPU的工作睦刃。
視圖懶加載 - iOS只會當視圖控制器的視圖顯示到屏幕上時才會加載它。這對內(nèi)存使用和程序啟動時間很有好處十酣,但是當呈現(xiàn)到屏幕上之前涩拙,按下按鈕導致的許多工作都會不能被及時響應兴泥。比如控制器從數(shù)據(jù)庫中獲取數(shù)據(jù)文留,或者視圖從一個nib文件中加載,或者涉及IO的圖片顯示(見后續(xù)“IO相關操作”),都會比CPU正常操作慢得多。
Core Graphics繪制 - 如果對視圖實現(xiàn)了-drawRect:方法台谍,或者CALayerDelegate的-drawLayer:inContext:方法掷伙,那么在繪制任何東西之前都會產(chǎn)生一個巨大的性能開銷沛厨。為了支持對圖層內(nèi)容的任意繪制页屠,Core Animation必須創(chuàng)建一個內(nèi)存中等大小的寄宿圖片况鸣。然后一旦繪制結束之后臭增,必須把圖片數(shù)據(jù)通過IPC傳到渲染服務器拗窃。在此基礎上宾毒,Core Graphics繪制就會變得十分緩慢癌瘾,所以在一個對性能十分挑剔的場景下這樣做十分不好咬荷。
解壓圖片 - PNG或者JPEG壓縮之后的圖片文件會比同質量的位圖小得多聚唐。但是在圖片繪制到屏幕上之前崖蜜,必須把它擴展成完整的未解壓的尺寸(通常等同于圖片寬 x 長 x 4個字節(jié))搏明。為了節(jié)省內(nèi)存,iOS通常直到真正繪制的時候才去解碼圖片(14章“圖片IO”會更詳細討論)粗悯。根據(jù)你加載圖片的方式虚循,第一次對圖層內(nèi)容賦值的時候(直接或者間接使用UIImageView)或者把它繪制到Core Graphics中横缔,都需要對它解壓,這樣的話衫哥,對于一個較大的圖片,都會占用一定的時間撤逢。
當圖層被成功打包,發(fā)送到渲染服務器之后蚊荣,CPU仍然要做如下工作:為了顯示屏幕上的圖層,Core Animation必須對渲染樹種的每個可見圖層通過OpenGL循環(huán)轉換成紋理三角板互例。由于GPU并不知曉Core Animation圖層的任何結構,所以必須要由CPU做這些事情媳叨。這里CPU涉及的工作和圖層個數(shù)成正比丁存,所以如果在你的層級關系中有太多的圖層,就會導致CPU沒一幀的渲染解寝,即使這些事情不是你的應用程序可控的。
IO相關操作
還有一項沒涉及的就是IO相關工作聋伦。上下文中的IO(輸入/輸出)指的是例如閃存或者網(wǎng)絡接口的硬件訪問。一些動畫可能需要從山村(甚至是遠程URL)來加載界睁。一個典型的例子就是兩個視圖控制器之間的過渡效果,這就需要從一個nib文件或者是它的內(nèi)容中懶加載翻斟,或者一個旋轉的圖片逾礁,可能在內(nèi)存中尺寸太大,需要動態(tài)滾動來加載访惜。
IO比內(nèi)存訪問更慢嘹履,所以如果動畫涉及到IO,就是一個大問題债热±担總的來說,這就需要使用聰敏但尷尬的技術窒篱,也就是多線程焕刮,緩存和投機加載(提前加載當前不需要的資源,但是之后可能需要用到)墙杯。這些技術將會在第14章中討論配并。
測量,而不是猜測
于是現(xiàn)在你知道有哪些點可能會影響動畫性能高镐,那該如何修復呢溉旋?好吧,其實不需要避消。有很多種詭計來優(yōu)化動畫低滩,但如果盲目使用的話,可能會造成更多性能上的問題岩喷,而不是修復恕沫。
如何正確的測量而不是猜測這點很重要。根據(jù)性能相關的知識寫出代碼不同于倉促的優(yōu)化纱意。前者很好婶溯,后者實際上就是在浪費時間。
那該如何測量呢?第一步就是確保在真實環(huán)境下測試你的程序迄委。
真機測試褐筛,而不是模擬器
當你開始做一些性能方面的工作時,一定要在真機上測試叙身,而不是模擬器渔扎。模擬器雖然是加快開發(fā)效率的一把利器晃痴,但它不能提供準確的真機性能參數(shù)财忽。
模擬器運行在你的Mac上,然而Mac上的CPU往往比iOS設備要快即彪。相反,Mac上的GPU和iOS設備的完全不一樣漏益,模擬器不得已要在軟件層面(CPU)模擬設備的GPU惠况,這意味著GPU相關的操作在模擬器上運行的更慢,尤其是使用CAEAGLLayer來些一些OpenGL的代碼時候。
這就是說在模擬器上的測試出的性能會高度失真翎苫。如果動畫在模擬器上運行流暢,可能在真機上十分糟糕攘蔽。如果在模擬器上運行的很卡呐粘,也可能在真機上很平滑。你無法確定唆垃。
另一件重要的事情就是性能測試一定要用發(fā)布配置痘儡,而不是調試模式。因為當用發(fā)布環(huán)境打包的時候渐尿,編譯器會引入一系列提高性能的優(yōu)化,例如去掉調試符號或者移除并重新組織代碼隘擎。你也可以自己做到這些凉夯,例如在發(fā)布環(huán)境禁用NSLog語句。你只關心發(fā)布性能宝惰,那才是你需要測試的點再沧。
最后,最好在你支持的設備中性能最差的設備上測試:如果基于iOS6開發(fā)淤堵,這意味著最好在iPhone 3GS或者iPad2上測試顷扩。如果可能的話,測試不同的設備和iOS版本扎阶,因為蘋果在不同的iOS版本和設備中做了一些改變,這也可能影響到一些性能东臀。例如iPad3明顯要在動畫渲染上比iPad2慢很多犀农,因為渲染4倍多的像素點(為了支持視網(wǎng)膜顯示)。
保持一致的幀率
為了做到動畫的平滑赁濒,你需要以60FPS(幀每秒)的速度運行孟害,以同步屏幕刷新速率。通過基于NSTimer或者CADisplayLink的動畫你可以降低到30FPS枝冀,而且效果還不錯,但是沒辦法通過Core Animation做到這點果漾。如果不保持60FPS的速率,就可能隨機丟幀吨凑,影響到體驗户辱。
你可以在使用的過程中明顯感到有沒有丟幀,但沒辦法通過肉眼來得到具體的數(shù)據(jù)恩商,也沒法知道你的做法有沒有真的提高性能必逆。你需要的是一系列精確的數(shù)據(jù)。
你可以在程序中用CADisplayLink來測量幀率(就像11章“基于定時器的動畫”中那樣)粟矿,然后在屏幕上顯示出來损拢,但應用內(nèi)的FPS顯示并不能夠完全真實測量出Core Animation性能,因為它僅僅測出應用內(nèi)的幀率掏秩。我們知道很多動畫都在應用之外發(fā)生(在渲染服務器進程中處理)隧膏,但同時應用內(nèi)FPS計數(shù)的確可以對某些性能問題提供參考,一旦找出一個問題的地方,你就需要得到更多精確詳細的數(shù)據(jù)來定位到問題所在魏宽。蘋果提供了一個強大的Instruments工具集來幫我們做到這些。
Instruments
Instruments是Xcode套件中沒有被充分利用的一個工具派桩。很多iOS開發(fā)者從沒用過Instruments,或者只是用Leaks工具檢測循環(huán)引用蚌斩。實際上有很多Instruments工具,包括為動畫性能調優(yōu)的東西员魏。
你可以通過在菜單中選擇Profile選項來打開Instruments(在這之前,記住要把目標設置成iOS設備受裹,而不是模擬器)棉饶。然后將會顯示出圖12.1(如果沒有看到所有選項镇匀,你可能設置成了模擬器選項)。
圖12.1 Instruments工具選項窗口
就像之前提到的那樣幸缕,你應該始終將程序設置成發(fā)布選項晃择。幸運的是,配置文件默認就是發(fā)布選項列疗,所以你不需要在分析的時候調整編譯策略浪蹂。
我們將討論如下幾個工具:
時間分析器 - 用來測量被方法/函數(shù)打斷的CPU使用情況。
Core Animation - 用來調試各種Core Animation性能問題古劲。
OpenGL ES驅動 - 用來調試GPU性能問題。這個工具在編寫Open GL代碼的時候很有用冰垄,但有時也用來處理Core Animation的工作。
Instruments的一個很棒的功能在于它可以創(chuàng)建我們自定義的工具集。除了你初始選擇的工具之外疑故,如果在Instruments中打開Library窗口,你可以拖拽別的工具到左側邊欄踱阿。我們將創(chuàng)建以上我們提到的三個工具,然后就可以并行使用了(見圖12.2)软舌。
圖12.2 添加額外的工具到Instruments側邊欄
時間分析器
時間分析器工具用來檢測CPU的使用情況葫隙。它可以告訴我們程序中的哪個方法正在消耗大量的CPU時間。使用大量的CPU并不一定是個問題 - 你可能期望動畫路徑對CPU非常依賴腺办,因為動畫往往是iOS設備中最苛刻的任務糟描。
但是如果你有性能問題,查看CPU時間對于判斷性能是不是和CPU相關躬拢,以及定位到函數(shù)都很有幫助(見圖12.3)见间。
圖12.3 時間分析器工具
時間分析器有一些選項來幫助我們定位到我們關心的的方法×馐撸可以使用左側的復選框來打開史侣。其中最有用的是如下幾點:
通過線程分離 - 這可以通過執(zhí)行的線程進行分組。如果代碼被多線程分離的話蚪腐,那么就可以判斷到底是哪個線程造成了問題税朴。
隱藏系統(tǒng)庫 - 可以隱藏所有蘋果的框架代碼,來幫助我們尋找哪一段代碼造成了性能瓶頸茧跋。由于我們不能優(yōu)化框架方法卓囚,所以這對定位到我們能實際修復的代碼很有用。
只顯示Obj-C代碼 - 隱藏除了Objective-C之外的所有代碼诅病。大多數(shù)內(nèi)部的Core Animation代碼都是用C或者C++函數(shù)粥烁,所以這對我們集中精力到我們代碼中顯式調用的方法就很有用。
Core Animation
Core Animation工具用來監(jiān)測Core Animation性能讨阻。它給我們提供了周期性的FPS篡殷,并且考慮到了發(fā)生在程序之外的動畫(見圖12.4)板辽。
圖12.4 使用可視化調試選項的Core Animation工具
Core Animation工具也提供了一系列復選框選項來幫助調試渲染瓶頸:
Color Blended Layers - 這個選項基于渲染程度對屏幕中的混合區(qū)域進行綠到紅的高亮(也就是多個半透明圖層的疊加)。由于重繪的原因耳标,混合對GPU性能會有影響邑跪,同時也是滑動或者動畫幀率下降的罪魁禍首之一。
ColorHitsGreenandMissesRed - 當使用shouldRasterizep屬性的時候砸琅,耗時的圖層繪制會被緩存症脂,然后當做一個簡單的扁平圖片呈現(xiàn)寇僧。當緩存再生的時候這個選項就用紅色對柵格化圖層進行了高亮。如果緩存頻繁再生的話兴蒸,就意味著柵格化可能會有負面的性能影響了(更多關于使用shouldRasterize的細節(jié)見第15章“圖層性能”)。
Color Copied Images - 有時候寄宿圖片的生成意味著Core Animation被強制生成一些圖片橙凳,然后發(fā)送到渲染服務器,而不是簡單的指向原始指針岛啸。這個選項把這些圖片渲染成藍色。復制圖片對內(nèi)存和CPU使用來說都是一項非常昂貴的操作坚踩,所以應該盡可能的避免瓤狐。
Color Immediately - 通常Core Animation Instruments以每毫秒10次的頻率更新圖層調試顏色批幌。對某些效果來說荧缘,這顯然太慢了拦宣。這個選項就可以用來設置每幀都更新(可能會影響到渲染性能绸罗,而且會導致幀率測量不準从诲,所以不要一直都設置它)靡羡。
Color Misaligned Images - 這里會高亮那些被縮放或者拉伸以及沒有正確對齊到像素邊界的圖片(也就是非整型坐標)。這些中的大多數(shù)通常都會導致圖片的不正趁璩叮縮放绽诚,如果把一張大圖當縮略圖顯示杭煎,或者不正確地模糊圖像,那么這個選項將會幫你識別出問題所在蜂桶。
Color Offscreen-Rendered Yellow - 這里會把那些需要離屏渲染的圖層高亮成黃色扑媚。這些圖層很可能需要用shadowPath或者shouldRasterize來優(yōu)化雷恃。
Color OpenGL Fast Path Blue - 這個選項會對任何直接使用OpenGL繪制的圖層進行高亮。如果僅僅使用UIKit或者Core Animation的API旬痹,那么不會有任何效果。如果使用GLKView或者CAEAGLLayer羡忘,那如果不顯示藍色塊的話就意味著你正在強制CPU渲染額外的紋理,而不是繪制到屏幕节猿。
Flash Updated Regions - 這個選項會對重繪的內(nèi)容高亮成黃色(也就是任何在軟件層面使用Core Graphics繪制的圖層)。這種繪圖的速度很慢峰鄙。如果頻繁發(fā)生這種情況的話吟榴,這意味著有一個隱藏的bug或者說通過增加緩存或者使用替代方案會有提升性能的空間囊扳。
這些高亮圖層的選項同樣在iOS模擬器的調試菜單也可用(圖12.5)。我們之前說過用模擬器測試性能并不好狭瞎,但如果你能通過這些高亮選項識別出性能問題出在什么地方的話搏予,那么使用iOS模擬器來驗證問題是否解決也是比真機測試更有效的。
圖12.5 iOS模擬器中Core Animation可視化調試選項
OpenGL ES驅動
OpenGL ES驅動工具可以幫你測量GPU的利用率雪侥,同樣也是一個很好的來判斷和GPU相關動畫性能的指示器。它同樣也提供了類似Core Animation那樣顯示FPS的工具(圖12.6)锌妻。
圖12.6 OpenGL ES驅動工具
側欄的郵編是一系列有用的工具鸟廓。其中和Core Animation性能最相關的是如下幾點:
Renderer Utilization - 如果這個值超過了~50%,就意味著你的動畫可能對幀率有所限制牍陌,很可能因為離屏渲染或者是重繪導致的過度混合员咽。
Tiler Utilization - 如果這個值超過了~50%贝室,就意味著你的動畫可能限制于幾何結構方面仿吞,也就是在屏幕上有太多的圖層占用了捡偏。
一個可用的案例
現(xiàn)在我們已經(jīng)對Instruments中動畫性能工具非常熟悉了银伟,那么可以用它在現(xiàn)實中解決一些實際問題。
我們創(chuàng)建一個簡單的顯示模擬聯(lián)系人姓名和頭像列表的應用傅物。注意即使把頭像圖片存在應用本地琉预,為了使應用看起來更真實,我們分別實時加載圖片卒暂,而不是用–imageNamed:預加載榨咐。同樣添加一些圖層陰影來使得列表顯示得更真實块茁。清單12.1展示了最初版本的實現(xiàn)。
清單12.1 使用假數(shù)據(jù)的一個簡單聯(lián)系人列表
#import?"ViewController.h"
#import?@interface?ViewController?()?@property?(nonatomic,?strong)?NSArray?*items;
@property?(nonatomic,?weak)?IBOutlet?UITableView?*tableView;
@end
@implementation?ViewController
-?(NSString?*)randomName
{
NSArray?*first?=?@[@"Alice",?@"Bob",?@"Bill",?@"Charles",?@"Dan",?@"Dave",?@"Ethan",?@"Frank"];
NSArray?*last?=?@[@"Appleseed",?@"Bandicoot",?@"Caravan",?@"Dabble",?@"Ernest",?@"Fortune"];
NSUInteger?index1?=?(rand()/(double)INT_MAX)?*?[first?count];
NSUInteger?index2?=?(rand()/(double)INT_MAX)?*?[last?count];
return[NSString?stringWithFormat:@"%@?%@",?first[index1],?last[index2]];
}
-?(NSString?*)randomAvatar
{
NSArray?*images?=?@[@"Snowman",?@"Igloo",?@"Cone",?@"Spaceship",?@"Anchor",?@"Key"];
NSUInteger?index?=?(rand()/(double)INT_MAX)?*?[images?count];
returnimages[index];
}
-?(void)viewDidLoad
{
[superviewDidLoad];
//set?up?data
NSMutableArray?*array?=?[NSMutableArray?array];
for(int?i?=?0;?i?<?1000;?i++)?{
?//add?name
[array?addObject:@{@"name":?[self?randomName],?@"image":?[self?randomAvatar]}];
}
self.items?=?array;
//register?cell?class
[self.tableView?registerClass:[UITableViewCell?class]?forCellReuseIdentifier:@"Cell"];
}
-?(NSInteger)tableView:(UITableView?*)tableView?numberOfRowsInSection:(NSInteger)section
{
return[self.items?count];
}
-?(UITableViewCell?*)tableView:(UITableView?*)tableView?cellForRowAtIndexPath:(NSIndexPath?*)indexPath
{
//dequeue?cell
UITableViewCell?*cell?=?[self.tableView?dequeueReusableCellWithIdentifier:@"Cell"forIndexPath:indexPath];
//load?image
NSDictionary?*item?=?self.items[indexPath.row];
NSString?*filePath?=?[[NSBundle?mainBundle]?pathForResource:item[@"image"]?ofType:@"png"];
//set?image?and?text
cell.imageView.image?=?[UIImage?imageWithContentsOfFile:filePath];
cell.textLabel.text?=?item[@"name"];
//set?image?shadow
cell.imageView.layer.shadowOffset?=?CGSizeMake(0,?5);
cell.imageView.layer.shadowOpacity?=?0.75;
cell.clipsToBounds?=?YES;
//set?text?shadow
cell.textLabel.backgroundColor?=?[UIColor?clearColor];
cell.textLabel.layer.shadowOffset?=?CGSizeMake(0,?2);
cell.textLabel.layer.shadowOpacity?=?0.5;
returncell;
}
@end
當快速滑動的時候就會非常卡(見圖12.7的FPS計數(shù)器)佩耳。
圖12.7 滑動幀率降到15FPS
僅憑直覺干厚,我們猜測性能瓶頸應該在圖片加載。我們實時從閃存加載圖片所坯,而且沒有緩存挂捅,所以很可能是這個原因。我們可以用一些很贊的代碼修復状土,然后使用GCD異步加載圖片蒙谓,然后緩存。彼乌。慰照。等一下毒租,在開始編碼之前箱叁,測試一下假設是否成立。首先用我們的三個Instruments工具分析一下程序來定位問題算色。我們推測問題可能和圖片加載相關螟够,所以用Time Profiler工具來試試(圖12.8)妓笙。
圖12.8 用The timing profile分析聯(lián)系人列表
-tableView:cellForRowAtIndexPath:中的CPU時間總利用率只有~28%(也就是加載頭像圖片的地方)萧福,非常低辈赋。于是建議是CPU/IO并不是真正的限制因素。然后看看是不是GPU的問題:在OpenGL ES Driver工具中檢測GPU利用率(圖12.9)钥屈。
圖12.9 OpenGL ES Driver工具顯示的GPU利用率
渲染服務利用率的值達到51%和63%焕蹄。看起來GPU需要做很多工作來渲染聯(lián)系人列表鸦泳。
為什么GPU利用率這么高呢?我們來用Core Animation調試工具選項來檢查屏幕击纬。首先打開Color Blended Layers(圖12.10)更振。
圖12.10 使用Color Blended Layers選項調試程序
屏幕中所有紅色的部分都意味著字符標簽視圖的高級別混合饭尝,這很正常,因為我們把背景設置成了透明色來顯示陰影效果实撒。這就解釋了為什么渲染利用率這么高了知态。
那么離屏繪制呢?打開Core Animation工具的Color Offscreen - Rendered Yellow選項(圖12.11)负敏。
圖12.11 Color Offscreen–Rendered Yellow選項
所有的表格單元內(nèi)容都在離屏繪制其做。這一定是因為我們給圖片和標簽視圖添加的陰影效果庶柿。在代碼中禁用陰影秽浇,然后看下性能是否有提高(圖12.12)。
圖12.12 禁用陰影之后運行程序接近60FPS
問題解決了审残。干掉陰影之后搅轿,滑動很流暢璧坟。但是我們的聯(lián)系人列表看起來沒有之前好了既穆。那如何保持陰影效果而且不會影響性能呢幻工?
好吧黎茎,每一行的字符和頭像在每一幀刷新的時候并不需要變傅瞻,所以看起來UITableViewCell的圖層非常時候做緩存。我們可以使用shouldRasterize來緩存圖層內(nèi)容胳挎。這將會讓圖層離屏之后渲染一次然后把結果保存起來溺森,直到下次利用的時候去更新(見清單12.2)。
清單12.2 使用shouldRasterize提高性能
-?(UITableViewCell?*)tableView:(UITableView?*)tableView?cellForRowAtIndexPath:(NSIndexPath?*)indexPath
{
//dequeue?cell
UITableViewCell?*cell?=?[self.tableView?dequeueReusableCellWithIdentifier:@"Cell"
forIndexPath:indexPath];
...
//set?text?shadow
cell.textLabel.backgroundColor?=?[UIColor?clearColor];
cell.textLabel.layer.shadowOffset?=?CGSizeMake(0,?2);
cell.textLabel.layer.shadowOpacity?=?0.5;
//rasterize
cell.layer.shouldRasterize?=?YES;
cell.layer.rasterizationScale?=?[UIScreen?mainScreen].scale;
returncell;
}
我們?nèi)匀浑x屏繪制圖層內(nèi)容,但是由于顯式地禁用了柵格化肾请,Core Animation就對繪圖緩存了結果铛铁,于是對提高了性能却妨。我們可以驗證緩存是否有效彪标,在Core Animation工具中點擊Color Hits Green and Misses Red選項(圖12.13)。
圖12.13 Color Hits Green and Misses Red驗證了緩存有效
結果和預期一致 - 大部分都是綠色薄声,只有當滑動到屏幕上的時候會閃爍成紅色题画。因此,現(xiàn)在幀率更加平滑了缩幸。
所以我們最初的設想是錯的。圖片的加載并不是真正的瓶頸所在钞护,而且試圖把它置于一個復雜的多線程加載和緩存的實現(xiàn)都將是徒勞患亿。所以在動手修復之前驗證問題所在是個很好的習慣押逼!
總結
在這章中挑格,我們學習了Core Animation是如何渲染,以及我們可能出現(xiàn)的瓶頸所在漂彤。你同樣學習了如何使用Instruments來檢測和修復性能問題雾消。