iOS性能優(yōu)化:Instrument 調(diào)試界面卡頓

前言

工欲善其事,必先利其器。Instrument對于iOS開發(fā)來說,是發(fā)現(xiàn)并且解決問題的一把利器。

本文會用到的兩個(gè)工具包括:

Time Profiler(獲取代碼運(yùn)行時(shí)間,一般用來看CPU占用)

Core Animation(獲取圖形繪制情況,F(xiàn)PS,離屏渲染等)

界面顯示的原理

iOS設(shè)備通常是60fps(每秒60幀),也就是說兩幀相隔的時(shí)間是1/60秒,大概16.7ms。在這16.7ms中,為了顯示一幀,需要如下工作

CPU計(jì)算好各個(gè)視圖的位置,大小,對圖片進(jìn)行解碼等,繪制成紋理交給GPU

GPU對收到的紋理進(jìn)行混合,頂點(diǎn)變換,渲染到幀緩沖區(qū)

每16.7ms,一個(gè)時(shí)鐘信號到達(dá),幀緩沖區(qū)取出一幀,顯示到屏幕。

也就是說,CPU或者GPU被大量占用的時(shí)候,都有可能在16.7ms中沒辦法完成一幀的繪制,導(dǎo)致時(shí)鐘信號到來的時(shí)候,取得還是上一幀的內(nèi)容,也就都有可能導(dǎo)致界面卡頓

離屏渲染

在iOS中,渲染通常分為CPU和GPU渲染兩種,而GPU渲染又分為在GPU緩沖區(qū)和非GPU緩沖區(qū)兩種

CPU渲染(軟件渲染),CPU繪制成bitmap,交給GPU

GPU渲染(硬件渲染)

GPU緩沖區(qū)渲染

非GPU緩沖區(qū)渲染(額外開辟緩沖區(qū))

通常,CPU渲染,和GPU非幀緩沖區(qū)內(nèi)渲染統(tǒng)稱為離屏渲染。因?yàn)?,CPU和幀緩沖區(qū)是為圖形圖像顯示做了高度優(yōu)化的,速度較快。

什么情況下會觸發(fā)離屏幕渲染?

用CoreGraphics的CGContext繪制的

在drawRect中繪制的,即使drawRect是空的

Layer具有Mask(比如圓角)或者Shadow

Layer的隔柵化shouldRasterize為True

文本(UILabel,UITextfield,UITextView,CoreText,UITextLayer等)

離屏渲染一定會引起性能問題嗎?

很少會,比如drawRect這個(gè)方法,只會在時(shí)圖進(jìn)行重新繪制的時(shí)候才會調(diào)用。也就是說,假如你的View并不會頻繁重繪,那么即使實(shí)現(xiàn)了drawRect,也沒什么關(guān)系。

對了,目前iOS設(shè)備的硬件越來越好也是一個(gè)原因,想要要性能差也挺難的。

CoreGraphics VS CALayer

上文提到了,CoreGraphics通常是CPU渲染成bitmap交給GPU,假如頻繁的大量的繪制出現(xiàn),往往會導(dǎo)致界面卡頓。而CALayer是對GPU做過優(yōu)化的,能夠硬件加速。所以,對于性能要求較高的繪制,嘗試用CALayer替代CoreGraphics

一個(gè)反面教材

一定要在真機(jī)上測試性能才有意義,本文是采用iPhone 5s來調(diào)試的。一般測試性能支持的性能最差的就可以了,如果是iOS 8要測試4s上的性能。

界面很簡單,一個(gè)ImageView,右側(cè)是隨機(jī)生成的100個(gè)字符,富文本顯示。


Time Profile

1.打開Time Profile,然后運(yùn)行想要分析的App


2.進(jìn)入主界面,上下滾動List,讓Time Profile采集數(shù)據(jù),

勾選右側(cè)的

Separate by Thread,按線程區(qū)分

Invert Call Tree ,逆向Call Tree,方便我們查看方法調(diào)用順序

Hide System Libraries,隱藏系統(tǒng)的庫,因?yàn)橥ǔO到y(tǒng)的代碼并不會影響性能


3.可以選擇一段時(shí)間,來分析這段時(shí)間CPU的使用情況


4.找到占用時(shí)間最多的代碼


然后,雙擊占用最多的這一行,進(jìn)入實(shí)際的代碼,看看到底哪里占用比較多


這里,我們看到是這一行代碼cell.testLabel?.attributedText = mutableAttr。

占用最多的CPU時(shí)間。

我們先來看下整個(gè)方法代碼,

TableViewCell其實(shí)很簡單,就一個(gè)ImageView(帶圓角,陰影),一個(gè)UILabel

cellForRowAtIndexPath里會隨機(jī)的生成100個(gè)字符,然后用AttributeText來讓UILabel顯示

乍一看,問題應(yīng)該是這個(gè)隨機(jī)生成100個(gè)字符的函數(shù)啊


因?yàn)?,每一次CellForRow調(diào)用的時(shí)候,都會計(jì)算100次。然后,我們實(shí)際分析的時(shí)候,發(fā)現(xiàn)其實(shí)100次對顯示來說,真不算什么,也不是卡頓的原因。

那么,為什么設(shè)置attributeText占用時(shí)間這么多呢?

其實(shí)很簡單,attributeText是建立在TextKit上的,由于每一次顯示都是隨機(jī)的attributeText,每一次都要重新計(jì)算文本的大小,位置等等。另外,UIKit中,提供的文本渲染都是在CPU中進(jìn)行的,渲染成Bitmap,然后交給GPU,所以導(dǎo)致設(shè)置attributeText的時(shí)候,占用很多時(shí)間。

這里不得不提到:一定不要過早優(yōu)化,優(yōu)化的時(shí)候盡量依賴于Instrument的分析結(jié)果,而不是自己的主觀感受。尤其當(dāng)你還是個(gè)新司機(jī)的時(shí)候。

Core Animation

在Instrument中,Command+L打開Library,然后添加Core Animation。我們來看看GPU的相關(guān)的問題

最直觀的就是滾動視圖,查看FPS(Frame per second),一般小于50幀就會看到明顯的掉幀。

1.看看圖層混合情況

只開啟Color Blended Layers,然后沒有混合的部分會是綠色,混合最嚴(yán)重的部分會是紅色。大量的圖層混合會消耗GPU的時(shí)間,因?yàn)閷τ谝粋€(gè)像素點(diǎn),GPU不能簡單的使用最上層的視圖的顏色,而是需要進(jìn)行計(jì)算疊加。

會看到截圖如下


這里的Cell整個(gè)背景都是紅的,因?yàn)楸尘笆莂lpha為0.3的View,UILabel是深紅色的,因?yàn)榇罅康年幱啊?/p>

2.看看隔柵化情況,

只開啟Color Hits Green and Misses Red,當(dāng)使用shouldRasterize屬性的時(shí)候,耗時(shí)的圖層繪制會被緩存,然后當(dāng)做一個(gè)簡單的扁平圖片呈現(xiàn)。當(dāng)緩存無法使用必須重建的時(shí)候,會被高亮為紅色。

截圖如下:

3.看看拷貝圖片情況

只開啟Color Copied Images- 有時(shí)候寄宿圖片的生成意味著Core Animation被強(qiáng)制生成一些圖片,然后發(fā)送到渲染服務(wù)器,而不是簡單的指向原始指針。這個(gè)選項(xiàng)把這些圖片渲染成藍(lán)色。復(fù)制圖片對內(nèi)存和CPU使用來說都是一項(xiàng)非常昂貴的操作,所以應(yīng)該盡可能的避免。

我的測試項(xiàng)目里沒有這個(gè),所以不貼圖了。

4.看看圖片有沒有像素不對齊,有沒有拉伸和縮放

Color Misaligned Images,可以看到如下。(因?yàn)槲覀兊目s略圖其實(shí)是一張很大的圖,所以被縮放了,導(dǎo)致顯示成黃色)

5.看看離屏渲染

-只開啟Color Offscreen-Rendered Yellow,離屏幕渲染的部分會被高亮成黃色

6.其他選項(xiàng)

Color Immediately 通常Core Animation Instruments以每毫秒10次的頻率更新圖層調(diào)試顏色。對某些效果來說,這顯然太慢了。這個(gè)選項(xiàng)就可以用來設(shè)置每幀都更新

Color OpenGL Fast Path Blue 這個(gè)選項(xiàng)會對任何直接使用OpenGL繪制的圖層進(jìn)行高亮

Flash Updated Region 這個(gè)選項(xiàng)會對重繪的內(nèi)容高亮成黃色(也就是任何在軟件層面使用Core Graphics繪制的圖層)。這種繪圖的速度很慢。

界面頓卡的原因

界面頓卡主要從兩個(gè)角度考慮

CPU限制

對象的創(chuàng)建,釋放,屬性調(diào)整。這里尤其要提一下屬性調(diào)整,CALayer的屬性調(diào)整的時(shí)候是會創(chuàng)建隱式動畫的,是比較損耗性能的。

視圖和文本的布局計(jì)算,AutoLayout的布局計(jì)算都是在主線程上的,所以占用CPU時(shí)間也很多 。U

文本渲染,諸如UILabel和UITextview都是在主線程渲染的

圖片的解碼,這里要提到的是,通常UIImage只有在交給GPU之前的一瞬間,CPU才會對其解碼。

GPU限制

視圖的混合。比如一個(gè)界面十幾層的視圖疊加到一起,GPU不得不計(jì)算每個(gè)像素點(diǎn)藥顯示的像素

離屏渲染。視圖的Mask,圓角,陰影。

半透明,GPU不得不進(jìn)行數(shù)學(xué)計(jì)算,如果是不透明的,CPU只需要取上層的就可以了

浮點(diǎn)數(shù)像素

界面頓卡的優(yōu)化

建議使用成熟的”輪子”,因?yàn)樽鳛橐粋€(gè)開發(fā)者,你的工作是寫出高質(zhì)量的App,那么為什么不用那些已經(jīng)驗(yàn)證成功的框架呢?如果真的輪子不能實(shí)現(xiàn),或者你有閑下來的時(shí)間,再造輪子未嘗不可。

使用AsyncDisplayKit

使用FaceBook出品的AsyncDisplayKit來寫復(fù)雜的界面。能夠獲得異步繪制,預(yù)先加載等諸多好處。不過,需要一定的學(xué)習(xí)成本,前段時(shí)間看了下網(wǎng)易新聞的安裝包,就使用了AsyncDisplayKit

圖文混排引擎

大多數(shù)性能要求較高的界面就是圖文混排,比如微博Feed,微信朋友圈等界面。建議使用成熟的圖文混排引擎,因?yàn)檫@些引擎一般支持異步繪制,并且做了大量優(yōu)化。推薦兩個(gè)

YYKit

DTCoreText

異步繪制

把復(fù)雜的界面,放到后臺線程里繪制成一個(gè)bitmap,然后再顯示。雖然有些延遲,不過換來的卻是平滑的界面。

圖片的解碼

建議使用成熟的庫,比如SDWebImage等,能夠在后臺進(jìn)行圖片解碼,減少CPU的使用。

預(yù)加載與緩存

對于復(fù)雜的TableView,可以對Cell視圖的各個(gè)控件的大小,位置后臺進(jìn)行預(yù)計(jì)算,并且緩存起來。這樣保證在heightForRow和cellForRow中不進(jìn)行大量的計(jì)算。

盡量使用CALayer

因?yàn)長ayer是一個(gè)輕量級的視圖結(jié)構(gòu),它不接受通知,不接受觸摸,不在響應(yīng)鏈。所以,相對于UIView來說,它的性能較好。并且CALayer及其子類是可以使用GPU渲染的,能夠硬件加速。

圖層預(yù)合成

將兩個(gè)CALayer的內(nèi)容合成到一個(gè)Bitmap里,然后顯示。能夠減輕GPU的壓力


上圖為2017年最新的視頻教程資料,搜索2352149755加我好友私聊我上傳視頻教程,有什么不懂的也可以來私聊問我。

不定時(shí)更新中。

如果你能明白這些視頻資料的好差,那么你也算是入行了,底層和中高層就是這一步之差。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容