UI卡頓&掉幀的原因
一般頁面滑動的幀數(shù)在60fps以內(nèi)時椿猎,頁面才會流暢,也就是說每一秒內(nèi)有60次的畫面更新残拐,相當(dāng)于在每隔16.7ms產(chǎn)生一幀畫面晾咪,在16.7ms內(nèi) CPU 和 GPU 要完成一幀畫面的繪制與渲染。
-
正常顯示:
比如說 CPU 要在一定時間內(nèi)完成視圖的文本的布局鞋既、視圖的繪制以及圖片的解碼等工作力九,最終將位圖 (bitmap) 提交給 GPU ,然后 GPU 完成視圖的渲染邑闺,最終在16.7ms內(nèi)提交給視頻控制器顯示在屏幕上跌前。
-
掉幀
如果說 CPU 在16.7ms內(nèi)占用的時間過多,最終導(dǎo)致 GPU 在16.7ms以后才將圖片渲染完提交給視頻控制器陡舅,就會導(dǎo)致掉幀舒萎,UI界面的卡頓
UIView的繪制原理
當(dāng)調(diào)用 UIView 的 方法時并不會立即繪制視圖,它會先調(diào)用視圖所對應(yīng)的的 layer 的同名方法
方法蹭沛,相當(dāng)于在當(dāng)前視圖上打上了個臟標(biāo)記臂寝,然后在當(dāng)前 runloop 將要結(jié)束時調(diào)用 CALyer 的
方法,最后才會進入繪制流程當(dāng)中摊灭。
在調(diào)用 CALyer 的 方法時咆贬,首先會先判斷是否響應(yīng) layer 的
代理方法,如果不響應(yīng)則進入系統(tǒng)繪制流程帚呼,當(dāng)響應(yīng)時掏缎,則進入異步繪制入口。
系統(tǒng)繪制流程
- CALayer 會回先創(chuàng)建一個 backing store(CGContextRef) 的上下文煤杀,然后會在 drawRect 方法中會在上下文堆棧中拿到 backing store
- 判斷是否 CALayer 是否有代理
- 如果沒有代理眷蜈,則調(diào)用系統(tǒng)的
方法
- 如果有代理,則會調(diào)用 layer.delegate 的
方法沈自,該方法發(fā)生自系統(tǒng)內(nèi)部酌儒,最后在合適的時機會調(diào)用
方法,該方法默認是什么都不做的
- 最后都會將 backing store (位圖)上傳至GPU中枯途,最后結(jié)束繪制
異步繪制
- [layer.delegate displayerLayer:]
- 代理負責(zé)生成對應(yīng)的bitmap
- 設(shè)置該bitmap作為layer.contents屬性的值
- 假如說在某個時間調(diào)用
方法
- 然后在當(dāng)前 runloop 將要結(jié)束的時候忌怎,會由系統(tǒng)調(diào)用視圖所對應(yīng)的
方法,如果我們的 delegate 方法實現(xiàn)了
方法酪夷,則由系統(tǒng)調(diào)用該方法
- 然后會通過子線程的切換榴啸,在子線程中實現(xiàn)視圖的繪制
- 在子線程中創(chuàng)建位圖的上下文(
),然后做UI的繪制工作(
)晚岭,然后根據(jù)繪制的上下文生成一張 CGImage 圖片(
)
- 最后回到主隊列中鸥印,將該 CGImage 圖片設(shè)置為 CALayer 的 contents
離屏渲染
-
在屏渲染(On-Screen Rendering)
指的是 的渲染操作是在用于當(dāng)前屏幕顯示的緩沖區(qū)中進行的
-
離屏渲染(Off-Screen Rendering)
指的是 在當(dāng)前屏幕緩沖區(qū)以外開辟一個
進行渲染操作
-
離屏渲染何時觸發(fā)
1、設(shè)置圓角(同時設(shè)置maskToBounds時才會觸發(fā))
2、設(shè)置視圖的圖層蒙版
3库说、設(shè)置陰影
4狂鞋、設(shè)置光柵化
-
為何要避免離屏渲染
離屏渲染會導(dǎo)致 GPU 增加額外的開銷,會導(dǎo)致在CPU和GPU處理的視圖的總時長增加璃弄,超過16.7ms要销,可能會導(dǎo)致視圖的卡頓和掉幀
滑動優(yōu)化方案
-
CPU:
1构回、對象的創(chuàng)建夏块、調(diào)整、銷毀
2纤掸、預(yù)排版(布局計算脐供、文本計算等)
3、預(yù)渲染(文本的異步繪制借跪、圖片的編解碼等)
-
GPU:
1政己、避免離屏渲染
2、避免視圖混合(視圖層級太多)