在 APP 內部的有4個階段
- ** 布局** :在這個階段,程序設置 View / Layer 的層級信息,設置 layer 的屬性,如 frame,background color 等等。
- ** 創建 backing image** :在這個階段程序會創建 layer 的 backing image,無論是通過 setContents 將一個 image 傳給 layer,還是通過 [drawRect:] 或 [drawLayer: inContext:] 來畫出來的。所以 [drawRect:] 等函數是在這個階段被調用的。
- ** 準備** :在這個階段,Core Animation 框架準備要渲染的 layer 的各種屬性數據,以及要做的動畫的參數,準備傳遞給 render server。同時在這個階段也會解壓要渲染的 image。(除了用 imageNamed:方法從 bundle 加載的 image 會立刻解壓之外,其他的比如直接從硬盤讀入,或者從網絡上下載的 image 不會立刻解壓,只有在真正要渲染的時候才會解壓)。
- ** 提交** :在這個階段,Core Animation 打包 layer 的信息以及需要做的動畫的參數,通過 IPC(inter-Process Communication)傳遞給 render server。
說明:在 iOS上,動畫和視圖的渲染其實是在另外一個進程做的(下面我們叫這個進程 render server),在 iOS 5 以前這個進程叫 SpringBoard,在 iOS 6 之后叫 BackBoard。
在 APP 外部的2個階段
當這些數據到達 render server 后,會被反序列化成 render tree。然后 render server 會做下面的兩件事:
- 根據 layer 的各種屬性(如果是動畫的,會計算動畫 layer 的屬性的中間值),用 OpenGL 準備渲染。
- 渲染這些可視的 layer 到屏幕。
掉幀原因
如果做動畫的話,最后的兩個步驟會一直重復直到動畫結束。 iOS 設備的屏幕刷新頻率是 60HZ,如果上面的這些步驟在一個刷新周期之內無法做完(1/60s),就會造成掉幀。有哪些操作可能會過度消耗 CPU 或者 GPU,從而造成掉幀呢?
視圖上有太多的 layer 或者幾何形狀:如果視圖的層級結構太復雜的話,當某些視圖被渲染或者 frame 被修改的話,CPU 會花比較多得時間去重新計算 frame。尤其如果用 autolayout 的話,會更消耗 CPU。同時過多的幾何結構會大大增多需要渲染的 OpenGL
triangles 以及柵格化的操作(將 OpenGL
的 triangles 轉化成像素)太多的 overdraw:overdraw 是指一個像素點被多次地用顏色填充。這個主要是由于一些半透明的 layer 相互重疊造成的。GPU 的 fill-rate(用顏色填充像素的速率)是有限的。如果 overdraw 太多的話,勢必會降低 GPU 的性能。
視圖的延后載入:iOS 只有在展示 viewcontroller 的 view 或者訪問 viewcontroller 的 view,比如說 someviewcontroller.view 的時候才會加載view。如果在用戶點擊了某個 button,并且在 button 的響應函數里做了很多消耗 cpu 的工作,這個時候如果 present 某個 viewcontroller 的話,會容易卡頓,尤其是如果viewcontroller 要從 database 里獲取數據,或者從 nib 文件初始化 view 或者加載圖片會更卡。
離屏的繪制:離屏的繪制有兩種情況:1. 有些效果(如 rounded corners,layer masks,drop shadows 和 layer rasterization)不能直接的繪制到屏幕上,必須先繪制到一個 offscreen 的 image context 上,這種操作會引入額外的內存和 CPU 消耗。2. 實現了 drawRect 或者 drawLayer:inContext:,為了支持任意的繪制,core graphic 會創建一個大小跟要畫的 view 一樣的 backing image。并且當畫完的以后要傳輸到 render server 上渲染。所以沒事不要重載 drawRect 等函數卻什么都不做。
圖片解壓:用 imageNamed:從 bundle 里加載會立馬解壓。一般的情況是在賦值給 UIImageView 的 image 或者 layer 的 contents 或者畫到一個 core graphic context 里才會解壓。
渲染性能優化的注意點
- 隱藏的繪制:contextlayer 和 UILabel 都是將 text 畫入 backingImage 的。如果改了一個包含 text 的 view 的 frame 的話,text 會被重新繪制。
- Rasterize:當使用 layer 的 shouldRasterize 的時候(記得設置適當的 laye r的 rasterizationScale),layer 會被強制繪制到一個 offscreen image 上,并且會被緩存起來。這種方法可以用來緩存繪制耗時(比如有比較絢的效果)但是不經常改的 layer,如果 layer 經常變,就不適合用。
- 離屏繪制: 使用Rounded corner, layer masks, drop shadows 的效果可以使用 stretchable images。比如實現 rounded corner,可以將一個圓形的圖片賦值于 layer 的 content 的屬性。并且設置好 contentsCenter 和 contentScale 屬性。
- Blending and Overdraw :如果一個 layer 被另一個 layer 完全遮蓋,GPU 會做優化不渲染被遮蓋的 layer,但是計算一個 layer 是否被另一個 layer 完全遮蓋是很耗 cpu 的。將幾個半透明的 layer 的 color 融合在一起也是很消耗的。
我們要做的:設置 view 的 backgroundColor 為一個固定的、不透明的 color。如果一個 view 是不透明的,設置 opaque 屬性為 YES(直接告訴程序這個是不透明的,而不是讓程序去計算)。這樣會減少 blending 和 overdraw。如果使用 image 的話,盡量避免設置 image 的 alpha 為透明的,如果一些效果需要幾個圖片融合而成,就讓設計用一張圖畫好,不要讓程序在運行的時候去動態的融合。
參考:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=297&fromuid=79