在iOS開發中,保持界面流暢和良好的用戶體驗是很是重要的,那麼界面的優化對咱們來講就是一個老生常談的話題,這裏總結了網上的一些資料和本身的一些開發經驗,講講界面性能的一些技巧.緩存
VSync
垂直信號,咱們通常說頁面滑動的流暢是60FPS,指的就是每秒鐘會有60 幀的畫面更新,咱們在人眼上看到的就是流暢的效果,那基於此呢,每一個16.7ms(1/60s)就要產生一幀畫面,那麼在這16.7ms中就須要由CPU和GPU共同協同完成產生最終的一幀的數據並經過數模轉換傳遞給顯示器顯示. 其中CPU負責計算顯示內容,好比視圖的建立、佈局計算、圖片解碼、文本繪製等,隨後把產生的位圖提交給GPU,再由GPU進行圖層的合成、紋理渲染等.而後GPU將渲染結果提交到幀緩衝區去,等待下一次 VSync 信號到來時顯示到屏幕上.而若是這個時候CPU或GPU處理的時間過長,當下一次VSync到來的時候,GPU尚未將渲染結果提交到幀緩衝區中去,那麼這一幀就會被丟棄,畫面繼續保持以前的內容,等下下次VSync信號到來的時候(若是此時GPU已將渲染結果提交到了幀緩衝區中),再顯示並刷新界面.因此原本16.7ms後就該更新一幀畫面的,如今用了33.4ms(可能更多),這就是卡頓產生的緣由. 那麼界面優化就主要從CPU和GPU兩個層面來優化了.
視圖的佈局計算是界面優化的一個重點,通常對於視圖佈局的優化能夠經過提早計算好視圖佈局和對視圖佈局進行緩存.好比cell的高度、label的行高等涉及到計算的.對於比較複雜的界面,若是使用Autolayout
也會帶來性能上的問題,Masonry
這個庫就是基於Autolayout
.由於過多的約束帶來的計算量是很是大的,給CPU的消耗很是大.那麼這就容易形成CPU運算時間超時產生卡頓.若是用Frame
的話會好不少.這篇文章比較了Frame和Autolayout對性能的影響. 可使用SDAutoLayout
這個庫,它的佈局是基於Frame的,或者使用 ComponentKit、AsyncDisplayKit 等框架.一樣Ulabel的寬高和繪製方法[NSAttributedString boundingRectWithSize:options:context:]
和 [NSAttributedString drawWithRect:options:context:]
也能夠放在後臺線程進行以免阻塞主線程.bash
對象的建立會分配內存、調整屬性、甚至還有讀取文件等操做,比較消耗 CPU 資源,因此將對象的建立放到子線程中去作會提升部分性能.經過 Storyboard 建立視圖對象還會涉及到文件反序列化操做,比起代碼建立的視圖,Storyboard要消耗更多的資源,因此對於一些對性能比較高的界面最好是經過代碼來進行建立和佈局.若是沒有涉及到觸摸事件等操做,能夠用CALayer替代UIView,由於CALayer相對於UIView來講要輕量不少.框架
如上圖所示,屏幕上能看到的全部文本內容控件,包括 UIWebView,在底層都是經過 CoreText 排版、繪製爲 Bitmap 顯示的。常見的文本控件 (UILabel、UITextView 等),其排版和繪製都是在主線程進行的,當顯示大量文本時,CPU 的壓力會很是大.因此能夠自定義控件,直接使用 CoreText 進行排版控制,不過這樣太麻煩了,反正我不會這麼作,哈哈哈.async
圖像的繪製是須要消耗CPU資源的,將繪製過程放在後臺線程中進行,而後再在主線程中將結果設置到layer的contents中,這樣會提升CPU的效率.代碼以下:佈局
- (void)display {
dispatch_async(backgroundQueue, ^{
CGContextRef ctx = CGBitmapContextCreate(...);
CGImageRef img = CGBitmapContextCreateImage(ctx);
CFRelease(ctx);
dispatch_async(mainQueue, ^{
layer.contents = img;
});
});
}
複製代碼
圖像的繪製一般是指用那些以 CG 開頭的方法把圖像繪製到畫布中,而後從畫布建立圖片並顯示的過程。前面的模塊圖裏介紹了 CoreGraphic 是做用在 CPU 之上的,所以調用 CG 開頭的方法消耗的是 CPU 資源。咱們能夠將繪製過程放到後臺線程,而後在主線程裏將結果設置到 layer 的 contents 中.性能
相對於 CPU 來講,GPU 能幹的事情比較單一:主要也就是紋理(圖片)和形狀(三角模擬的矢量圖形)兩類。優化
全部的 Bitmap,包括圖片、文本、柵格化的內容,最終都要由內存提交到顯存,綁定爲 GPU Texture。不管是提交到顯存的過程,仍是 GPU 調整和渲染 Texture 的過程,都要消耗很多 GPU 資源。當在較短期顯示大量圖片時(好比 TableView 存在很是多的圖片而且快速滑動時),CPU 佔用率很低,GPU 佔用很是高,界面仍然會掉幀.spa
當多個視圖(或者說 CALayer)重疊在一塊兒顯示時,GPU 會首先把他們混合到一塊兒。若是視圖結構過於複雜,混合的過程也會消耗不少 GPU 資源。爲了減輕這種狀況的 GPU 消耗,應用應當儘可能減小視圖數量和層次,並在不透明的視圖裏標明 opaque 屬性以免無用的 Alpha 通道合成。固然,這也能夠用上面的方法,把多個視圖預先渲染爲一張圖片來顯示.線程
CALayer 的 border、圓角、陰影、遮罩(mask),CASharpLayer 的矢量圖形顯示,一般會觸發離屏渲染(offscreen rendering),而離屏渲染一般發生在 GPU 中。當一個列表視圖中出現大量圓角的 CALayer,而且快速滑動時,能夠觀察到 GPU 資源已經佔滿,而 CPU 資源消耗不多。這時界面仍然能正常滑動,但平均幀數會降到很低。爲了不這種狀況,能夠嘗試開啓 CALayer.shouldRasterize 屬性,但這會把本來離屏渲染的操做轉嫁到 CPU 上去。對於只須要圓角的某些場合,也能夠用一張已經繪製好的圓角圖片覆蓋到本來視圖上面來模擬相同的視覺效果。最完全的解決辦法,就是把須要顯示的圖形在後臺線程繪製爲圖片.設置瞭如下屬性時,都會觸發離屏渲染:code
layer.shouldRasterize
,光柵化
layer.mask
,遮罩
layer.allowsGroupOpacity
爲YES,layer.opacity
的值小於1.0
layer.cornerRadius
,而且設置layer.masksToBounds
爲YES
。可使用剪切過的圖片,或者使用layer畫來解決
layer.shadows
,(表示相關的shadow開頭的屬性),使用shadowPath
代替