iOS介面優化原理及方案
本文內容是參考文章:http://www.cocoachina.com/ios/20151130/14477.html
*寫本篇文章的目的為了總結和梳理上面那篇 ofollow,noindex">文章 (YYKit的作者寫的)的知識點,我並不是想秀~~~~
一.螢幕顯示影象的原理
基本上所有的顯示器都是遵循CRT顯示器原理,在顯示一幀影象的時候大致是一把電子槍經過從左至右、從上到下的順序進行掃描,掃描完成後顯示器就會顯示完整的一幀畫面。

借用一下~~嘻嘻
1、CPU計算顯示內容傳遞給GPU。
2、GPU渲染完成後將這一幀的內容放到幀緩衝區。(這裡有兩個幀快取區,GPU會根據訊號來回切換)
3、視訊控制器取出緩衝區的內容並轉換傳遞給顯示器。(這裡視訊控制器是通過垂直水平訊號VSync獲取緩衝區資料)
4、顯示器顯示完成後併發送VSync訊號給GPU。
5、跳到第二步進行迴圈。
二、手機介面卡頓的原因
顯示器在顯示完一幀之後,傳送垂直同步訊號到CPU、GPU,如果這時候CPU和GPU在訊號時間內沒有完成計算或渲染內容,也就是快取區沒有內容可取,此時顯示器還是會顯示上一幀的內容,這時手機介面就會給人以卡頓的感覺。這也就是卡頓的主要原因了。
此時如果要對介面優化,主要就是針對CPU和GPU資源消耗進行優化了。
也許有人不知道CPU核GPU的區別(我就是其中一個),我查了下。CPU又叫中央處理器,GPU叫圖形處理器。
——CPU比較適合處理複雜的邏輯控制,通用性更強,能夠處理很多不同的資料型別。
——GPU適合影象處理和大規模併發計算。

它們的內部結構大概是這樣(繼續偷圖)
想了解更多點選 這裡 。
三、CPU資源消耗解決方案
在程式執行中比較消耗CPU資源的主要是:
1、物件建立
消耗資源:
——物件的建立會分配記憶體、調整屬性、檔案操作。
優化方案:
——儘量使用輕量的物件代替重量級的物件(如:輕量級CALayer、重量級UIView)
——儘量推遲物件的建立時間,並把物件的建立分散到多個任務中去(CALayer、UIVIew的建立操作只能在主執行緒)。
——如果物件可以複用並且複用的代價比釋放重新建立要小就儘量複用。
2、物件調整
消耗資源:
——CALayer內部並沒有屬性,在呼叫屬性方法時候,它內部是通過執行時resolveInstanceMethod為物件臨時新增一個方法,並把對應的屬性儲存到內部的字典裡面,同時還會通知代理、建立動畫等等。所以在使用UIView的 顯示相關屬性 時其實都是CALayer屬性對映來的。
優化方案:
——儘量減少顯示相關屬性不必要的修改。
——儘量避免檢視層次調整、新增和移除。
3、物件銷燬
消耗資源:
——只有當一個物件容器持有大量物件時,銷燬時的資源消耗比較明顯。
優化方案:
——如果物件可以放到後臺執行緒去釋放,就儘量放到後臺去釋放。
4、佈局計算
消耗資源:
——不論用什麼技術對檢視進行佈局,終究還是改變UIVIew的frame/bounds/center 等屬性的調整。
優化方案:
——在後臺執行緒提前計算好佈局,在需要時一次性計算調整好對應屬性,不要多次、頻繁的計算和調整。
消耗資源:
——不論用什麼技術對檢視進行佈局,終究還是改變UIVIew的frame/bounds/center 等屬性的調整。
優化方案:
——在後臺執行緒提前計算好佈局,在需要時一次性計算調整好對應屬性,不要多次、頻繁的計算和調整。
6、文字計算
消耗資源:
——如果一個介面中包含大量文字,文字的寬高計算會佔用很大一部分資源,並且不可避免。
優化方案:
——如果對文字顯示沒有特殊要求,可以參考下 UILabel 內部的實現方式:用 [NSAttributedString boundingRectWithSize:options:context:] 來計算文字寬高,用 -[NSAttributedString drawWithRect:options:context:] 來繪製文字。儘管這兩個方法效能不錯,但仍舊需要放到後臺執行緒進行以避免阻塞主執行緒。
——如果用 CoreText 繪製文字,那就可以先生成 CoreText 排版物件,然後自己計算了,並且 CoreText 物件還能保留以供稍後繪製使用。
7、文字渲染
消耗資源:
——螢幕上能看到的所有文字內容控制元件,包括 UIWebView,在底層都是通過 CoreText 排版、繪製為 Bitmap 顯示的。常見的文字控制元件 (UILabel、UITextView 等),其排版和繪製都是在主執行緒進行的,當顯示大量文字時,CPU 的壓力會非常大。
優化方案:
——解決方案只有一個,那就是自定義文字控制元件,用 TextKit 或最底層的 CoreText 對文字非同步繪製。CoreText 物件建立好後,能直接獲取文字的寬高等資訊,避免了多次計算(調整 UILabel 大小時算一遍、UILabel 繪製時內部再算一遍);CoreText 物件佔用記憶體較少,可以快取下來以備稍後多次渲染。
8、圖片的解碼
消耗資源:
——用 UIImage 或 CGImageSource 的那幾個方法建立圖片時,圖片資料並不會立刻解碼。圖片設定到 UIImageView 或者 CALayer.contents 中去,並且 CALayer 被提交到 GPU 前,CGImage 中的資料才會得到解碼。這一步是發生在主執行緒的,並且不可避免。
優化方案:
——在後臺執行緒先把圖片繪製到 CGBitmapContext 中,然後從 Bitmap 直接建立圖片。目前常見的網路圖片庫都自帶這個功能。
9、影象的繪製
消耗資源:
——影象的繪製通常是指用那些以 CG 開頭的方法把影象繪製到畫布中,然後從畫布建立圖片並顯示這樣一個過程。這個最常見的地方就是 [UIView drawRect:] 裡面了。
優化方案:
——由於 CoreGraphic 方法通常都是執行緒安全的,所以影象的繪製可以很容易的放到後臺執行緒進行。
四、GPU資源消耗解決方案
1、紋理的渲染
消耗資源:
——所有的 Bitmap,包括圖片、文字、柵格化的內容,最終都要由記憶體提交到視訊記憶體,繫結為 GPU Texture。不論是提交到視訊記憶體的過程,還是 GPU 調整和渲染 Texture 的過程,都要消耗不少 GPU 資源。當在較短時間顯示大量圖片時(比如 TableView 存在非常多的圖片並且快速滑動時),CPU 佔用率很低,GPU 佔用非常高,介面仍然會掉幀。
——當圖片過大,超過 GPU 的最大紋理尺寸時,圖片需要先由 CPU 進行預處理,這對 CPU 和 GPU 都會帶來額外的資源消耗。
優化方案:
——儘量減少在短時間內大量圖片的顯示,儘可能將多張圖片合成為一張進行顯示。
——目前來說,iPhone 4S 以上機型,紋理尺寸上限都是 4096x4096,更詳細的資料可以看這裡:iosres.com。所以,儘量不要讓圖片和檢視的大小超過這個值。
2、檢視的混合
消耗資源:
——當多個檢視(或者說 CALayer)重疊在一起顯示時,GPU 會首先把他們混合到一起。如果檢視結構過於複雜,混合的過程也會消耗很多 GPU 資源。
優化方案:
——為了減輕這種情況的 GPU 消耗,應用應當儘量減少檢視數量和層次,並在不透明的視圖裡標明 opaque 屬性以避免無用的 Alpha 通道合成。當然,這也可以用上面的方法,把多個檢視預先渲染為一張圖片來顯示。
3、圖片的形成
消耗資源:
——CALayer 的 border、圓角、陰影、遮罩(mask),CASharpLayer 的向量圖形顯示,通常會觸發離屏渲染(offscreen rendering),而離屏渲染通常發生在 GPU 中。當一個列表檢視中出現大量圓角的 CALayer,並且快速滑動時,可以觀察到 GPU 資源已經佔滿,而 CPU 資源消耗很少。這時介面仍然能正常滑動,但平均幀數會降到很低。
優化方案:
——可以嘗試開啟 CALayer.shouldRasterize 屬性,但這會把原本離屏渲染的操作轉嫁到 CPU 上去。對於只需要圓角的某些場合,也可以用一張已經繪製好的圓角圖片覆蓋到原本檢視上面來模擬相同的視覺效果。最徹底的解決辦法,就是把需要顯示的圖形在後臺執行緒繪製為圖片,避免使用圓角、陰影、遮罩等屬性。
最後作者推薦了個框架AsyncDisplayKit。該框架對CALayer進行了封裝,當使用該框架封裝的view時,裡面將消耗效能的操作放在後臺執行緒進行處理,並且節省了資源。
但是在平時日常的開發中,一般是沒有時間進行優化的,最好是將優化工作放到後期處理。