渲染視圖的核心是:Core Animation
-
底層->上層:
GPU->(OpenGL、Core Graphic)->Core Animation->UIKit
- 在 iOS 上,動畫和視圖的渲染其實是在另外一個進程做的羹蚣,即可稱為:render server秩铆。(ios5以前:SpringBoard倒庵;ios6后:BackBoard)
iOS 上視圖或者動畫渲染的各個階段
在 APP 內(nèi)部有4個階段:
-
布局
在這個階段怕午,程序設(shè)置 View / Layer 的層級信息陋守,設(shè)置 layer 的屬性震贵,如 frame,background color 等等水评。
-
創(chuàng)建 backing image
在這個階段程序會創(chuàng)建 layer 的 backing image猩系,無論是通過 setContents 將一個 image 傳給 layer,還是通過 [drawRect:] 或 [drawLayer: inContext:] 來畫出來的中燥。所以 [drawRect:] 等函數(shù)是在這個階段被調(diào)用的寇甸。
-
準(zhǔn)備
在這個階段,Core Animation 框架準(zhǔn)備要渲染的 layer 的各種屬性數(shù)據(jù)疗涉,以及要做的動畫的參數(shù)拿霉,準(zhǔn)備傳遞給 render server。同時在這個階段也會解壓要渲染的 image咱扣。(除了用 imageNamed:方法從 bundle 加載的 image 會立刻解壓之外绽淘,其他的比如直接從硬盤讀入,或者從網(wǎng)絡(luò)上下載的 image 不會立刻解壓闹伪,只有在真正要渲染的時候才會解壓)沪铭。
-
提交
在這個階段,Core Animation 打包 layer 的信息以及需要做的動畫的參數(shù)祭往,通過 IPC(inter-Process Communication)傳遞給 render server伦意。
數(shù)據(jù)到達 render server 后火窒,會被反序列化成 render tree硼补。
之后,在 APP 外部有2個階段:
- 根據(jù) layer 的各種屬性(如果是動畫的熏矿,會計算動畫 layer 的屬性的中間值)已骇,用 OpenGL 準(zhǔn)備渲染离钝。
- 渲染這些可視的 layer 到屏幕。
掉幀效果
我們都知道 iOS 設(shè)備的屏幕刷新頻率是 60Hz褪储。如果上面的這些步驟在一個刷新周期之內(nèi)無法做完(1/60s)卵渴,就會造成掉幀。
多度消耗 CPU 和 GPU 的掉幀行為:
-
視圖上有太多的 layer 或者幾何形狀(CPU&GPU)
如果視圖的層級結(jié)構(gòu)太復(fù)雜的話鲤竹,當(dāng)某些視圖被渲染或者 frame 被修改的話浪读,CPU 會花比較多得時間去重新計算 frame。尤其如果用 autolayout 的話辛藻,會更消耗 CPU碘橘。同時過多的幾何結(jié)構(gòu)會大大增多需要渲染的 OpenGLl triangles 以及柵格化的操作(將 OpenGL 的 triangles 轉(zhuǎn)化成像素)
-
太多的 overdraw:overdraw 是指一個像素點被多次地用顏色填充.(GPU)
這個主要是由于一些半透明的 layer 相互重疊造成的。GPU 的 fill-rate(用顏色填充像素的速率)是有限的吱肌。如果 overdraw 太多的話痘拆,勢必會降低 GPU 的性能。
-
視圖的延后載入:(CPU)
iOS 只有在展示 viewcontroller 的 view 或者訪問 viewcontroller 的 view氮墨,比如說 someviewcontroller.view 的時候才會加載view纺蛆。如果在用戶點擊了某個 button,并且在 button 的響應(yīng)函數(shù)里做了很多消耗 cpu 的工作规揪,這個時候如果 present 某個 viewcontroller 的話桥氏,會容易卡頓,尤其是如果 viewcontroller 要從 database 里獲取數(shù)據(jù)猛铅,或者從 nib 文件初始化 view 或者加載圖片會更卡识颊。
-
離屏的繪制(GPU):
離屏的繪制有兩種情況:
有些效果(如 rounded corners,layer masks奕坟,drop shadows 和 layer rasterization)不能直接的繪制到屏幕上祥款,必須先繪制到一個 offscreen 的 image context 上,這種操作會引入額外的內(nèi)存和 CPU 消耗月杉。
實現(xiàn)了 drawRect 或者 drawLayer:inContext:刃跛,為了支持任意的繪制,core graphic 會創(chuàng)建一個大小跟要畫的 view 一樣的 backing image苛萎。并且當(dāng)畫完的以后要傳輸?shù)?render server 上渲染桨昙。所以沒事不要重載 drawRect 等函數(shù)卻什么都不做。
-
圖片解壓:
用 imageNamed:從 bundle 里加載會立馬解壓腌歉。一般的情況是在賦值給 UIImageView 的 image 或者 layer 的 contents 或者畫到一個 core graphic context 里才會解壓蛙酪。
渲染性能的優(yōu)化
-
隱藏的繪制:
catextlayer 和 uilabel 都是將 text 畫入 backing image 的。如果改了一個包含 text 的 view 的 frame 的話翘盖,text 會被重新繪制桂塞。
-
Rasterize:
當(dāng)使用 layer 的 shouldRasterize 的時候(記得設(shè)置適當(dāng)?shù)?laye r的 rasterizationScale),layer 會被強制繪制到一個 offscreen image 上馍驯,并且會被緩存起來阁危。這種方法可以用來緩存繪制耗時(比如有比較絢的效果)但是不經(jīng)常改的 layer玛痊,如果 layer 經(jīng)常變,就不適合用狂打。
-
離屏繪制:
使用 Rounded corner擂煞, layer masks, drop shadows 的效果可以使用 stretchable images趴乡。比如實現(xiàn) rounded corner对省,可以將一個圓形的圖片賦值于 layer 的 content 的屬性。并且設(shè)置好 contentsCenter 和 contentScale 屬性晾捏。
-
Blending and Overdraw:
如果一個 layer 被另一個 layer 完全遮蓋官辽,GPU 會做優(yōu)化不渲染被遮蓋的 layer,但是計算一個 layer 是否被另一個 layer 完全遮蓋是很耗 cpu 的粟瞬。將幾個半透明的 layer 的 color 融合在一起也是很消耗的同仆。