iOS 性能優化總結

卡頓產生的緣由

 

 

在 VSync信號到來後,系統圖形服務會經過 CADisplayLink等機制通知 AppApp主線程開始在 CPU中計算顯示內容,好比視圖的建立、佈局計算、圖片解碼、文本繪製等。隨後 CPU會將計算好的內容提交到 GPU去,由 GPU進行變換、合成、渲染。隨後 GPU會把渲染結果提交到幀緩衝區去,等待下一次 VSync信號到來時顯示到屏幕上。因爲垂直同步的機制,若是在一個 VSync時間內,CPU或者 GPU沒有完成內容提交,則那一幀就會被丟棄,等待下一次機會再顯示,而這時顯示屏會保留以前的內容不變。這就是界面卡頓的緣由。ios

在開發中,CPUGPU中任何一個壓力過大,都會致使掉幀現象,因此在開發時,也須要分別對CPUGPU壓力進行評估和優化。git

iOS 設備中的 CPU & GPU

CPU

加載資源,對象建立,對象調整,對象銷燬,佈局計算,Autolayout,文本計算,文本渲染,圖片的解碼, 圖像的繪製(Core Graphics)都是在CPU上面進行的。github

GPU

GPU是一個專門爲圖形高併發計算而量身定作的處理單元,比CPU使用更少的電來完成工做而且GPU的浮點計算能力要超出CPU不少。算法

GPU的渲染性能要比CPU高效不少,同時對系統的負載和消耗也更低一些,因此在開發中,咱們應該儘可能讓CPU負責主線程的UI調動,把圖形顯示相關的工做交給GPU來處理,當涉及到光柵化等一些工做時,CPU也會參與進來,這點在後面再詳細描述。緩存

相對於CPU來講,GPU能幹的事情比較單一:接收提交的紋理(Texture)和頂點描述(三角形),應用變換(transform)、混合(合成)並渲染,而後輸出到屏幕上。一般你所能看到的內容,主要也就是紋理(圖片)和形狀(三角模擬的矢量圖形)兩類。性能優化

CPU 和 GPU 的協做

 

 

由上圖可知,要在屏幕上顯示視圖,須要CPUGPU一塊兒協做,CPU計算好顯示的內容提交到GPUGPU渲染完成後將結果放到幀緩存區,隨後視頻控制器會按照 VSync信號逐行讀取幀緩衝區的數據,通過可能的數模轉換傳遞給顯示器顯示。session

緩衝機制

 

 

iOS使用的是雙緩衝機制。即GPU會預先渲染好一幀放入一個緩衝區內(前幀緩存),讓視頻控制器讀取,當下一幀渲染好後,GPU會直接把視頻控制器的指針指向第二個緩衝器(後幀緩存)。當你視頻控制器已經讀完一幀,準備讀下一幀的時候,GPU會等待顯示器的VSync信號發出後,前幀緩存和後幀緩存會瞬間切換,後幀緩存會變成新的前幀緩存,同時舊的前幀緩存會變成新的後幀緩存。併發

優化方案

YY大神的 iOS 保持界面流暢的技巧中詳細介紹了 CPU 資源消耗緣由和解決方案和 GPU 資源消耗緣由和解決方案,這裏麪包括了開發中的大部分場景,能夠幫助咱們快速定位卡頓的緣由,迅速解決卡頓。框架

下面是一些常見的優化方案!異步

TableViewCell 複用

cellForRowAtIndexPath:回調的時候只建立實例,快速返回cell,不綁定數據。在willDisplayCell: forRowAtIndexPath:的時候綁定數據(賦值)。

高度緩存

tableView滑動時,會不斷調用heightForRowAtIndexPath:,當 cell高度須要自適應時,每次回調都要計算高度,會致使 UI 卡頓。爲了不重複無心義的計算,須要緩存高度。

怎麼緩存?

視圖層級優化

不要動態建立視圖
  • 在內存可控的前提下,緩存subview
  • 善用hidden
減小視圖層級
  • 減小subviews個數,用layer繪製元素。
  • 少用 clearColormaskToBounds,陰影效果等。
減小多餘的繪製操做

圖片

  • 不要用JPEG的圖片,應當使用PNG圖片。
  • 子線程預解碼(Decode),主線程直接渲染。由於當image沒有Decode,直接賦值給imageView會進行一個Decode操做。
  • 優化圖片大小,儘可能不要動態縮放(contentMode)。
  • 儘量將多張圖片合成爲一張進行顯示。

減小透明 view

使用透明view會引發blending,在iOS的圖形處理中,blending主要指的是混合像素顏色的計算。最直觀的例子就是,咱們把兩個圖層疊加在一塊兒,若是第一個圖層的透明的,則最終像素的顏色計算須要將第二個圖層也考慮進來。這一過程即爲Blending

會致使blending的緣由:

  • UIViewalpha1
  • UIImageViewimage含有alpha channel(即便UIImageViewalpha1,但只要image含有透明通道,則仍會致使blending)。

爲何blending會致使性能的損失?

緣由是很直觀的,若是一個圖層是不透明的,則系統直接顯示該圖層的顏色便可。而若是圖層是透明的,則會引發更多的計算,由於須要把另外一個的圖層也包括進來,進行混合後的顏色計算。

  • opaque設置爲YES,減小性能消耗,由於GPU將不會作任何合成,而是簡單從這個層拷貝。

減小離屏渲染

離屏渲染指的是在圖像在繪製到當前屏幕前,須要先進行一次渲染,以後才繪製到當前屏幕。

OpenGL中,GPU屏幕渲染有如下兩種方式:

  • On-Screen Rendering即當前屏幕渲染,指的是GPU的渲染操做是在當前用於顯示的屏幕緩衝區中進行。

  • Off-Screen Rendering即離屏渲染,指的是GPU在當前屏幕緩衝區之外新開闢一個緩衝區進行渲染操做。

爲何離屏渲染會發生卡頓?主要包括兩方面內容:

  • 建立新的緩衝區。
  • 上下文切換,離屏渲染的整個過程,須要屢次切換上下文環境(CPU渲染和GPU切換),先是從當前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結束之後,將離屏緩衝區的渲染結果顯示到屏幕上又須要將上下文環境從離屏切換到當前屏幕。而上下文環境的切換是要付出很大代價的。

設置瞭如下屬性時,都會觸發離屏渲染:

  • layer.shouldRasterize,光柵化

  • layer.mask,遮罩

  • layer.allowsGroupOpacityYESlayer.opacity的值小於1.0

  • layer.cornerRadius,而且設置layer.masksToBoundsYES。可使用剪切過的圖片,或者使用layer畫來解決。

  • layer.shadows,(表示相關的shadow開頭的屬性),使用shadowPath代替。

    兩種不一樣方式來繪製陰影: 不使用shadowPath

     

     

    使用shadowPath

     

     

    性能差異,以下圖:

     

離屏渲染的優化建議

  • 使用ShadowPath指定layer陰影效果路徑。
  • 使用異步進行layer渲染(Facebook開源的異步繪製框架AsyncDisplayKit)。
  • 設置layeropaque值爲YES,減小複雜圖層合成。
  • 儘可能使用不包含透明(alpha)通道的圖片資源。
  • 儘可能設置layer的大小值爲整形值。
  • 直接讓美工把圖片切成圓角進行顯示,這是效率最高的一種方案。
  • 不少狀況下用戶上傳圖片進行顯示,能夠在客戶端處理圓角。
  • 使用代碼手動生成圓角image設置到要顯示的View上,利用UIBezierPathCore Graphics框架)畫出來圓角圖片。

合理使用光柵化 shouldRasterize

光柵化是把GPU的操做轉到CPU上,生成位圖緩存,直接讀取複用。

優勢:
  • CALayer會被光柵化爲bitmapshadowscornerRadius等效果會被緩存。
缺點:
  • 更新已經光柵化的layer,會形成離屏渲染。
  • bitmap超過100ms沒有使用就會移除。
  • 受系統限制,緩存的大小爲 2.5X Screen Size。

shouldRasterize適合靜態頁面顯示,動態頁面會增長開銷。若是設置了shouldRasterize爲 YES,那也要記住設置rasterizationScalecontentsScale

異步渲染

在子線程繪製,主線程渲染。例如 VVeboTableViewDemo

 

 

理性使用-drawRect:

你們或許感到奇怪,有很多開發者在發有關性能優化的博客當中指出使用-drawRect:來優化性能。可是我這裏不太建議你們未經思考的使用-drawRect:方法。緣由以下:

當你使用UIImageView在加載一個視圖的時候,這個視圖雖然依然有CALayer,可是卻沒有申請到一個後備的存儲,取而代之的是使用一個使用屏幕外渲染,將CGImageRef做爲內容,並用渲染服務將圖片數據繪製到幀的緩衝區,就是顯示到屏幕上,當咱們滾動視圖的時候,這個視圖將會從新加載,浪費性能。因此對於使用-drawRect:方法,更傾向於使用CALayer來繪製圖層。由於使用CALayer-drawInContext:Core Animation將會爲這個圖層申請一個後備存儲,用來保存那些方法繪製進來的位圖。那些方法內的代碼將會運行在 CPU上,結果將會被上傳到GPU。這樣作的性能更爲好些。

靜態界面建議使用-drawRect:的方式,動態頁面不建議。

按需加載

  • 局部刷新,刷新一個cell就能解決的,堅定不刷新整個 section或者整個tableView刷新最小單元元素
  • 利用runloop提升滑動流暢性,在滑動中止的時候再加載內容,像那種一閃而過的(快速滑動),就沒有必要加載,可使用默認的佔位符填充內容。

關於性能測試

在出現圖像性能問題,滑動,動畫不夠流暢以後,咱們首先要作的就是定位出問題的所在。而這個過程並非只靠經驗和窮舉法探索,咱們應該用有脈絡,有順序的科學的手段進行探索。

首先,咱們要有一個定位問題的模式。咱們能夠按照這樣的順序來逐步定位,發現問題。

  1. 定位幀率,爲了給用戶流暢的感覺,咱們須要保持幀率在60幀左右。當遇到問題後,咱們首先檢查一下幀率是否保持在60幀。
  2. 定位瓶頸,到底是CPU仍是GPU。咱們但願佔用率越少越好,一是爲了流暢性,二也節省了電力。
  3. 檢查有沒有作無必要的CPU渲染,例若有些地方咱們重寫了drawRect:,而實際上是咱們不須要也不該該的。咱們但願GPU負責更多的工做。
  4. 檢查有沒有過多的離屏渲染,這會耗費GPU的資源,像前面已經分析的到的。離屏渲染會致使GPU須要不斷地onScreenoffscreen進行上下文切換。咱們但願有更少的離屏渲染。
  5. 檢查咱們有無過多的BlendingGPU渲染一個不透明的圖層更省資源。
  6. 檢查圖片的格式是否爲經常使用格式,大小是否正常。若是一個圖片格式不被GPU所支持,則只能經過CPU來渲染。通常咱們在iOS開發中都應該用PNG格式,以前閱讀過的一些資料也有指出蘋果特地爲PNG格式作了渲染和壓縮算法上的優化。
  7. 檢查是否有耗費資源多的View或效果,咱們須要合理有節制的使用。
  8. 最後,咱們須要檢查在咱們View層級中是否有不正確的地方。例若有時咱們不斷的添加或移除View,有時就會在不經意間致使bug的發生。
測試工具:
  • Core AnimationInstruments裏的圖形性能問題的測試工具。
  • view debugging,Xcode 自帶的,視圖層級。
  • reveal,視圖層級。

參考文章

做者:LaiYoung_ 連接:https://juejin.im/post/5ace078cf265da23994ee493 來源:掘金 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
相關文章
相關標籤/搜索