界面優(yōu)化
圖片顯示到屏幕上是CPU與GPU的協(xié)作完成
- CPU: 計算視圖frame涣易,圖片解碼,需要繪制紋理圖片通過數(shù)據(jù)總線交給GPU
- GPU: 紋理混合泥张,頂點變換與計算,像素點的填充計算详恼,渲染到幀緩沖區(qū)他嚷。
- 時鐘信號:垂直同步信號V-Sync / 水平同步信號H-Sync默刚。
-
iOS設(shè)備雙緩沖機制:顯示系統(tǒng)通常會引入兩個幀緩沖區(qū)甥郑,雙緩沖機制
二.圖片加載的工作流程
- 假設(shè)我們使用 +imageWithContentsOfFile: 方法從磁盤中加載一張圖片,這個時候的圖片并沒有解壓縮荤西;
- 然后將生成的 UIImage 賦值給 UIImageView
- 接著一個隱式的 CATransaction 捕獲到了 UIImageView 圖層樹的變化澜搅;
- 在主線程的下一個 runloop 到來時,Core Animation 提交了這個隱式的 transaction 邪锌,這個過程可能會對圖片進行 copy 操作勉躺,而受圖片是否字節(jié)對齊等因素的影響,這個 copy 操作可能會涉及以下部分或全部步驟:
- 分配內(nèi)存緩沖區(qū)用于管理文件 IO 和解壓縮操作秃流;
- 將文件數(shù)據(jù)從磁盤讀到內(nèi)存中赂蕴;
- 將壓縮的圖片數(shù)據(jù)解碼成未壓縮的位圖形式柳弄,這是一個非常耗時的 CPU 操作舶胀;
- 最后 Core Animation 中CALayer使用未壓縮的位圖數(shù)據(jù)渲染 UIImageView 的圖層。
- CPU計算好圖片的Frame,對圖片解壓之后.就會交給GPU來做圖片渲染
- 渲染流程:
- GPU獲取獲取圖片的坐標
- 將坐標交給頂點著色器(頂點計算)
- 將圖片光柵化(獲取圖片對應(yīng)屏幕上的像素點)
- 片元著色器計算(計算每個像素點的最終顯示的顏色值)
- 從幀緩存區(qū)中渲染到屏幕上
我們提到了圖片的解壓縮是一個非常耗時的 CPU 操作碧注,并且它默認是在主線程中執(zhí)行的嚣伐。那么當(dāng)需要加載的圖片比較多時,就會對我們應(yīng)用的響應(yīng)性造成嚴重的影響萍丐,尤其是在快速滑動的列表上轩端,這個問題會表現(xiàn)得更加突出。
卡頓監(jiān)控
FPS監(jiān)控:為了保持流程的UI交互逝变,App的刷新拼搏應(yīng)該保持在60fps左右基茵,其原因是因為iOS設(shè)備默認的刷新頻率是60次/秒,而1次刷新(即VSync信號發(fā)出)的間隔是 1000ms/60 = 16.67ms壳影,所以如果在16.67ms內(nèi)沒有準備好下一幀數(shù)據(jù)拱层,就會產(chǎn)生卡頓
主線程卡頓監(jiān)控:通過子線程監(jiān)測主線程的RunLoop,判斷兩個狀態(tài)(kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting)之間的耗時是否達到一定閾值
界面優(yōu)化
CPU層面的優(yōu)化
1宴咧、盡量用輕量級的對象代替重量級的對象根灯,可以對性能有所優(yōu)化,例如 不需要相應(yīng)觸摸事件的控件,用CALayer代替UIView
2烙肺、盡量減少對UIView和CALayer的屬性修改
- CALayer內(nèi)部并沒有屬性纳猪,當(dāng)調(diào)用屬性方法時,其內(nèi)部是通過運行時resolveInstanceMethod為對象臨時添加一個方法桃笙,并將對應(yīng)屬性值保存在內(nèi)部的一個Dictionary中氏堤,同時還會通知delegate、創(chuàng)建動畫等怎栽,非常耗時
- UIView相關(guān)的顯示屬性丽猬,例如frame、bounds熏瞄、transform等脚祟,實際上都是從CALayer映射來的,對其進行調(diào)整時强饮,消耗的資源比一般屬性要大
3由桌、當(dāng)有大量對象釋放時,也是非常耗時的邮丰,盡量挪到后臺線程去釋放
4行您、盡量提前計算視圖布局,即預(yù)排版剪廉,例如cell的行高
5娃循、Autolayout在簡單頁面情況下們可以很好的提升開發(fā)效率,但是對于復(fù)雜視圖而言斗蒋,會產(chǎn)生嚴重的性能問題捌斧,隨著視圖數(shù)量的增長,Autolayout帶來的CPU消耗是呈指數(shù)上升的泉沾。所以盡量使用代碼布局捞蚂。如果不想手動調(diào)整frame等,也可以借助三方庫跷究,例如Masonry(OC)姓迅、SnapKit(Swift)、ComponentKit俊马、AsyncDisplayKit等
6丁存、文本處理的優(yōu)化:當(dāng)一個界面有大量文本時,其行高的計算柴我、繪制也是非常耗時的
1)如果對文本沒有特殊要求解寝,可以使用UILabel內(nèi)部的實現(xiàn)方式,且需要放到子線程中進行屯换,避免阻塞主線程
計算文本寬高:[NSAttributedString boundingRectWithSize:options:context:]
文本繪制:[NSAttributedString drawWithRect:options:context:]
2)自定義文本控件编丘,利用TextKit 或最底層的 CoreText 對文本異步繪制与学。并且CoreText 對象創(chuàng)建好后,能直接獲取文本的寬高等信息嘉抓,避免了多次計算(調(diào)整和繪制都需要計算一次)索守。CoreText直接使用了CoreGraphics占用內(nèi)存小,效率高
7抑片、圖片處理(解碼 + 繪制)
1)當(dāng)使用UIImage 或 CGImageSource 的方法創(chuàng)建圖片時卵佛,圖片的數(shù)據(jù)不會立即解碼,而是在設(shè)置時解碼(即圖片設(shè)置到UIImageView/CALayer.contents中敞斋,然后在CALayer提交至GPU渲染前截汪,CGImage中的數(shù)據(jù)才進行解碼)。這一步是無可避免的植捎,且是發(fā)生在主線程中的衙解。想要繞開這個機制,常見的做法是在子線程中先將圖片繪制到CGBitmapContext焰枢,然后從Bitmap 直接創(chuàng)建圖片蚓峦,例如SDWebImage三方框架中對圖片編解碼的處理。這就是Image的預(yù)解碼
當(dāng)使用CG開頭的方法繪制圖像到畫布中济锄,然后從畫布中創(chuàng)建圖片時暑椰,可以將圖像的繪制在子線程中進行
8、圖片優(yōu)化
1)盡量使用PNG圖片荐绝,不使用JPGE圖片
2)通過子線程預(yù)解碼一汽,主線程渲染,即通過Bitmap創(chuàng)建圖片低滩,在子線程賦值image
3)優(yōu)化圖片大小召夹,盡量避免動態(tài)縮放
4)盡量將多張圖合為一張進行顯示
9、盡量避免使用透明view委造,因為使用透明view戳鹅,會導(dǎo)致在GPU中計算像素時均驶,會將透明view下層圖層的像素也計算進來昏兆,即顏色混合處理,可以參考六妇穴、OpenGL 渲染技巧:深度測試爬虱、多邊形偏移、 混合這篇文章中提及的混合
10腾它、按需加載跑筝,例如在TableView中滑動時不加載圖片,使用默認占位圖瞒滴,而是在滑動停止時加載
11曲梗、少使用addView 給cell動態(tài)添加view
GPU層面優(yōu)化
1赞警、盡量減少在短時間內(nèi)大量圖片的顯示,盡可能將多張圖片合為一張顯示虏两,主要是因為當(dāng)有大量圖片進行顯示時愧旦,無論是CPU的計算還是GPU的渲染,都是非常耗時的定罢,很可能出現(xiàn)掉幀的情況
2笤虫、盡量避免圖片的尺寸超過4096×4096,因為當(dāng)圖片超過這個尺寸時祖凫,會先由CPU進行預(yù)處理琼蚯,然后再提交給GPU處理,導(dǎo)致額外CPU資源消耗
3惠况、盡量減少視圖數(shù)量和層次遭庶,主要是因為視圖過多且重疊時,GPU會將其混合稠屠,混合的過程也是非常耗時的
4罚拟、盡量避免離屏渲染,可以查看這篇文章四完箩、深入剖析【離屏渲染】原理
5赐俗、異步渲染,例如可以將cell中的所有控件弊知、視圖合成一張圖片進行顯示阻逮。可以參考Graver三方框架