iOS 視圖、動畫渲染機制探究

騰訊 Bugly 特約做者:陳向文緩存

終端的開發,首當其衝的就是視圖、動畫的渲染,切換等等。用戶使用 App 時最直接的體驗就是這個界面好很差看,動畫炫不炫,滑動流不流暢。UI就是 App 的門面,它的體驗伴隨着用戶使用 App 的整個過程。若是UI失敗,用戶是不會有打開第二次的慾望的。性能優化

iOS 爲開發者提供了豐富的 Framework(UIKit,Core Animation,Core Graphic,OpenGL 等等)來知足開發從上層到底層各類各樣的需求。不得不說蘋果很牛逼,不少接口你根本不須要理解背後的原理就能上手使用而且知足你大部分的需求,可是,若是遇到性能問題就容易抓瞎。易用性跟優化就是個矛盾體,就像 ARC 同樣,當你沒有遇到內存問題的時候用得很爽,一旦遇到了,就要要求你比在用 MRC 的時候更加了解 iOS 的內存機制。UI 亦是如此。何況,做爲鵝廠的員工固然不能僅限於知道怎麼用。咱們要知其然還要知其因此然。好了,廢話不說,咱們進入主題:看看 iOS 是如何渲染視圖和動畫的,以及在咱們遇到渲染的性能問題時怎麼作優化。微信

(注意:如下內容是筆者的一些踩坑經驗和總結, 歡迎探討!)網絡

先來看看官方對 Core Animation 的一段說明:app

以看出iOS渲染視圖的核心是 Core Animation。從底層到上層依此是 GPU->(OpenGL、Core Graphic) -> Core Animation -> UIKit。框架

在 iOS上,動畫和視圖的渲染實際上是在另一個進程作的(下面咱們叫這個進程 render server),在 iOS 5 之前這個進程叫 SpringBoard,在 iOS 6 以後叫 BackBoard。函數

下面這幅圖是使用項目錄制視頻的時候(大量視圖渲染),整個系統的進程狀況:佈局

能夠很清楚地看到 BackBoard 這個進程的狀況。性能

iOS 上視圖或者動畫渲染的各個階段

在 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。

在 APP 外部的2個階段

當這些數據到達 render server 後,會被反序列化成 render tree。而後 render server 會作下面的兩件事:

  • 根據 layer 的各類屬性(若是是動畫的,會計算動畫 layer 的屬性的中間值),用 OpenGL 準備渲染。

  • 渲染這些可視的 layer 到屏幕。

若是作動畫的話,最後的兩個步驟會一直重複知道動畫結束。

咱們都知道 iOS 設備的屏幕刷新頻率是 60HZ。若是上面的這些步驟在一個刷新週期以內沒法作完(1/60s),就會形成掉幀。

咱們看看有哪些操做可能會過分消耗 CPU 或者 GPU,從而形成掉幀。

視圖上有太多的 layer 或者幾何形狀:若是視圖的層級結構太複雜的話,當某些視圖被渲染或者 frame 被修改的話,CPU 會花比較多得時間去從新計算 frame。尤爲若是用 autolayout 的話,會更消耗 CPU。同時過多的幾何結構會大大增多須要渲染的 OpenGLl 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 裏纔會解壓。

渲染性能優化的注意點

隱藏的繪製:catextlayer 和 uilabel 都是將 text 畫入 backing image 的。若是改了一個包含 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 融合在一塊兒也是很消耗的。

咱們要作的:

  1. 設置 view 的 backgroundColor 爲一個固定的,不透明的 color。

  2. 若是一個 view 是不透明的,設置 opaque 屬性爲 YES。(直接告訴程序這個是不透明的,而不是讓程序去計算)

這樣會減小 blending 和 overdraw。

若是使用 image 的話,儘可能避免設置 image 的 alpha 爲透明的,若是一些效果須要幾個圖片融合而成,就讓設計用一張圖畫好,不要讓程序在運行的時候去動態的融合。

好了,介紹完這些渲染優化須要注意的點,讓咱們用 instrument 的 Core Animation 和 GPU driver 來看看一些具體的例子。

Core Animation

Color Blended Layers:看半透明 layer 的遮蓋狀況。從綠到紅,越紅遮蓋越大。

這下這兩幅圖是測量項目詳情頁的半透明的layer的狀況。能夠看到詳情頁這裏半透明的layer仍是比較多的,但不是說半透明的layer不少,範圍很大就要優化,要參看GPU driver的測量狀況看,下面會介紹

Color Hits Green and Misses Red:當使用shouldRasterize的時候,layer drawing 會被緩存起來,若是 rasterized 的 layer 須要被從新繪製,會標示紅。

下面是全民K歌消息列表的 cell 設置 shouldRasterize=YES 的狀況,每一個 cell 的 layer 的 backing image 會被緩存起來,若是往下滾動 tableview 的話,因爲 cell 會被複用,這樣 layer 就會被重繪,會被標紅。

Color Offscreen-Rendered Yellow:若是要作 offscreen drawing 的話,會標成黃色。

下面是項目的首頁,因爲圓形頭像的實現方式是設置 roundedCorner 的屬性來實現,因此會觸發 offscreen drawing。

Core Animation template 只是能讓開發者直觀地看到哪些地方有可能須要優化,可是到底要不要優化,仍是要看 GPU driver 的表現。

GPU Driver

Renderer Utilization ——若是這個值大於50%的話,表示 GPU 的性能受到 fill-rate 的限制,可能有太多的 Offscreen rendering,overdraw,blending。

Tiler Utilization ——若是這個值大於50%,表示可能有太多的 layers。

咱們以上面的那個項目的詳情頁爲例,看看 GPU driver 的測量:

能夠看到這 Renderer Utilization 是20%左右,Tiler Utilization 時15%,能夠不用優化,(固然這裏咱們先不考慮 CPU 的使用狀況,只是單單針對上面 core animation template 的一些測量說不用作針對性的優化)

這裏想說的一點是,以上所說的點只是在咱們遇到渲染性能問題的時候給咱們提供優化的方向跟思路。優化每每表明着更復雜,難懂的代碼,在沒有遇到渲染性能問題的時候不要過分優化。

但願對你們有幫助!


本文爲騰訊Bugly原創文章,如需轉載,請標明出處。
想了解更多幹貨,請搜索關注公衆號:騰訊Bulgy,或搜索微信號:weixinBugly,關注咱們

騰訊 Bugly

Bugly 是騰訊內部產品質量監控平臺的外發版本,支持iOS和Android兩大主流平臺,其主要功能是App發佈之後,對用戶側發生的crash以及卡頓現象進行監控並上報,讓開發同窗能夠第一時間瞭解到app的質量狀況,及時修改。目前騰訊內部全部的產品,均在使用其進行線上產品的崩潰監控。騰訊內部團隊4年打磨,目前騰訊內部全部的產品都在使用,基本覆蓋了中國市場的移動設備以及網絡環境,可靠性有保證。使用Bugly,你就使用了和手機QQ、QQ空間、手機管家相同的質量保障手段

相關文章
相關標籤/搜索