本文內(nèi)容是參考文章:http://www.cocoachina.com/ios/20151130/14477.html
*寫(xiě)本篇文章的目的為了總結(jié)和梳理上面那篇文章(YYKit的作者寫(xiě)的)的知識(shí)點(diǎn),我并不是想秀~~~~
一.屏幕顯示圖像的原理
基本上所有的顯示器都是遵循CRT顯示器原理,在顯示一幀圖像的時(shí)候大致是一把電子槍經(jīng)過(guò)從左至右、從上到下的順序進(jìn)行掃描,掃描完成后顯示器就會(huì)顯示完整的一幀畫(huà)面。
總的來(lái)說(shuō),CPU、GPU、顯示器的協(xié)作方式大致是下圖方式:

1、CPU計(jì)算顯示內(nèi)容傳遞給GPU。
2、GPU渲染完成后將這一幀的內(nèi)容放到幀緩沖區(qū)。(這里有兩個(gè)幀緩存區(qū),GPU會(huì)根據(jù)信號(hào)來(lái)回切換)
3、視頻控制器取出緩沖區(qū)的內(nèi)容并轉(zhuǎn)換傳遞給顯示器。(這里視頻控制器是通過(guò)垂直水平信號(hào)VSync獲取緩沖區(qū)數(shù)據(jù))
4、顯示器顯示完成后并發(fā)送VSync信號(hào)給GPU。
5、跳到第二步進(jìn)行循環(huán)。
二、手機(jī)界面卡頓的原因
顯示器在顯示完一幀之后,發(fā)送垂直同步信號(hào)到CPU、GPU,如果這時(shí)候CPU和GPU在信號(hào)時(shí)間內(nèi)沒(méi)有完成計(jì)算或渲染內(nèi)容,也就是緩存區(qū)沒(méi)有內(nèi)容可取,此時(shí)顯示器還是會(huì)顯示上一幀的內(nèi)容,這時(shí)手機(jī)界面就會(huì)給人以卡頓的感覺(jué)。這也就是卡頓的主要原因了。
此時(shí)如果要對(duì)界面優(yōu)化,主要就是針對(duì)CPU和GPU資源消耗進(jìn)行優(yōu)化了。
也許有人不知道CPU核GPU的區(qū)別(我就是其中一個(gè)),我查了下。CPU又叫中央處理器,GPU叫圖形處理器。
? ? ——CPU比較適合處理復(fù)雜的邏輯控制,通用性更強(qiáng),能夠處理很多不同的數(shù)據(jù)類(lèi)型。
? ? ——GPU適合圖像處理和大規(guī)模并發(fā)計(jì)算。

想了解更多點(diǎn)擊這里。
三、CPU資源消耗解決方案
在程序運(yùn)行中比較消耗CPU資源的主要是:
1、對(duì)象創(chuàng)建
? ? 消耗資源:
????????????——對(duì)象的創(chuàng)建會(huì)分配內(nèi)存、調(diào)整屬性、文件操作。
? ? 優(yōu)化方案:
????????????——盡量使用輕量的對(duì)象代替重量級(jí)的對(duì)象(如:輕量級(jí)CALayer、重量級(jí)UIView)
????????????——盡量推遲對(duì)象的創(chuàng)建時(shí)間,并把對(duì)象的創(chuàng)建分散到多個(gè)任務(wù)中去(CALayer、UIVIew的創(chuàng)建操作只能在主線(xiàn)程)。
????????????——如果對(duì)象可以復(fù)用并且復(fù)用的代價(jià)比釋放重新創(chuàng)建要小就盡量復(fù)用。
2、對(duì)象調(diào)整
?????????????消耗資源:
????????????????????——CALayer內(nèi)部并沒(méi)有屬性,在調(diào)用屬性方法時(shí)候,它內(nèi)部是通過(guò)運(yùn)行時(shí)resolveInstanceMethod為對(duì)象臨時(shí)添加一個(gè)方法,并把對(duì)應(yīng)的屬性保存到內(nèi)部的字典里面,同時(shí)還會(huì)通知代理、創(chuàng)建動(dòng)畫(huà)等等。所以在使用UIView的顯示相關(guān)屬性時(shí)其實(shí)都是CALayer屬性映射來(lái)的。
????????? ? 優(yōu)化方案:????
????????????????????——盡量減少顯示相關(guān)屬性不必要的修改。
????????????????????——盡量避免視圖層次調(diào)整、添加和移除。
3、對(duì)象銷(xiāo)毀
? ??????????消耗資源:
????????????????????——只有當(dāng)一個(gè)對(duì)象容器持有大量對(duì)象時(shí),銷(xiāo)毀時(shí)的資源消耗比較明顯。
? ? ? ? ? ? 優(yōu)化方案:????
????????????????????——如果對(duì)象可以放到后臺(tái)線(xiàn)程去釋放,就盡量放到后臺(tái)去釋放。
4、布局計(jì)算
? ? ? ? ? ? ? ?消耗資源:
????????????????????——不論用什么技術(shù)對(duì)視圖進(jìn)行布局,終究還是改變UIVIew的frame/bounds/center 等屬性的調(diào)整。
? ? ? ? ? ? 優(yōu)化方案:????
????????????????????——在后臺(tái)線(xiàn)程提前計(jì)算好布局,在需要時(shí)一次性計(jì)算調(diào)整好對(duì)應(yīng)屬性,不要多次、頻繁的計(jì)算和調(diào)整。
5、Autolayout
? ? ? ? ? ? ? ?消耗資源:
????????????????????——不論用什么技術(shù)對(duì)視圖進(jìn)行布局,終究還是改變UIVIew的frame/bounds/center 等屬性的調(diào)整。
? ? ? ? ? ? 優(yōu)化方案:????
????????????????????——在后臺(tái)線(xiàn)程提前計(jì)算好布局,在需要時(shí)一次性計(jì)算調(diào)整好對(duì)應(yīng)屬性,不要多次、頻繁的計(jì)算和調(diào)整。
6、文本計(jì)算
? ? ? ? ? ??消耗資源:
? ? ? ? ? ? ? ? ? ? ——如果一個(gè)界面中包含大量文本,文本的寬高計(jì)算會(huì)占用很大一部分資源,并且不可避免。
? ? ? ? ? ? 優(yōu)化方案:????
????????????????????——如果對(duì)文本顯示沒(méi)有特殊要求,可以參考下 UILabel 內(nèi)部的實(shí)現(xiàn)方式:用 [NSAttributedString boundingRectWithSize:options:context:] 來(lái)計(jì)算文本寬高,用 -[NSAttributedString drawWithRect:options:context:] 來(lái)繪制文本。盡管這兩個(gè)方法性能不錯(cuò),但仍舊需要放到后臺(tái)線(xiàn)程進(jìn)行以避免阻塞主線(xiàn)程。
? ? ? ? ? ? ? ? ? ? ?——如果用 CoreText 繪制文本,那就可以先生成 CoreText 排版對(duì)象,然后自己計(jì)算了,并且 CoreText 對(duì)象還能保留以供稍后繪制使用。
7、文本渲染
? ??????????消耗資源:
? ? ? ? ? ? ? ? ? ? ——屏幕上能看到的所有文本內(nèi)容控件,包括 UIWebView,在底層都是通過(guò) CoreText 排版、繪制為 Bitmap 顯示的。常見(jiàn)的文本控件 (UILabel、UITextView 等),其排版和繪制都是在主線(xiàn)程進(jìn)行的,當(dāng)顯示大量文本時(shí),CPU 的壓力會(huì)非常大。
? ? ? ? ? ? 優(yōu)化方案:????
????????????????????——解決方案只有一個(gè),那就是自定義文本控件,用 TextKit 或最底層的 CoreText 對(duì)文本異步繪制。CoreText 對(duì)象創(chuàng)建好后,能直接獲取文本的寬高等信息,避免了多次計(jì)算(調(diào)整 UILabel 大小時(shí)算一遍、UILabel 繪制時(shí)內(nèi)部再算一遍);CoreText 對(duì)象占用內(nèi)存較少,可以緩存下來(lái)以備稍后多次渲染。
8、圖片的解碼
? ??????????????消耗資源:
????????????????????——用 UIImage 或 CGImageSource 的那幾個(gè)方法創(chuàng)建圖片時(shí),圖片數(shù)據(jù)并不會(huì)立刻解碼。圖片設(shè)置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 GPU 前,CGImage 中的數(shù)據(jù)才會(huì)得到解碼。這一步是發(fā)生在主線(xiàn)程的,并且不可避免。
? ? ? ? ? ? 優(yōu)化方案:????
????????????????????——在后臺(tái)線(xiàn)程先把圖片繪制到 CGBitmapContext 中,然后從 Bitmap 直接創(chuàng)建圖片。目前常見(jiàn)的網(wǎng)絡(luò)圖片庫(kù)都自帶這個(gè)功能。
9、圖像的繪制
? ? ? ? ? ? ? ?消耗資源:
????????????????????——圖像的繪制通常是指用那些以 CG 開(kāi)頭的方法把圖像繪制到畫(huà)布中,然后從畫(huà)布創(chuàng)建圖片并顯示這樣一個(gè)過(guò)程。這個(gè)最常見(jiàn)的地方就是 [UIView drawRect:] 里面了。
? ? ? ? ? ? 優(yōu)化方案:????
????????????????????——由于 CoreGraphic 方法通常都是線(xiàn)程安全的,所以圖像的繪制可以很容易的放到后臺(tái)線(xiàn)程進(jìn)行。
四、GPU資源消耗解決方案
1、紋理的渲染
? ??????????消耗資源:
????????????????????——所有的 Bitmap,包括圖片、文本、柵格化的內(nèi)容,最終都要由內(nèi)存提交到顯存,綁定為 GPU Texture。不論是提交到顯存的過(guò)程,還是 GPU 調(diào)整和渲染 Texture 的過(guò)程,都要消耗不少 GPU 資源。當(dāng)在較短時(shí)間顯示大量圖片時(shí)(比如 TableView 存在非常多的圖片并且快速滑動(dòng)時(shí)),CPU 占用率很低,GPU 占用非常高,界面仍然會(huì)掉幀。
? ? ? ? ? ? ? ? ? ? ——當(dāng)圖片過(guò)大,超過(guò) GPU 的最大紋理尺寸時(shí),圖片需要先由 CPU 進(jìn)行預(yù)處理,這對(duì) CPU 和 GPU 都會(huì)帶來(lái)額外的資源消耗。
? ? ? ? ? ? 優(yōu)化方案:????
????????????????????——盡量減少在短時(shí)間內(nèi)大量圖片的顯示,盡可能將多張圖片合成為一張進(jìn)行顯示。
? ? ? ? ? ? ? ? ? ? ——目前來(lái)說(shuō),iPhone 4S 以上機(jī)型,紋理尺寸上限都是 4096x4096,更詳細(xì)的資料可以看這里:iosres.com。所以,盡量不要讓圖片和視圖的大小超過(guò)這個(gè)值。
2、視圖的混合
? ? ? ? ? ??消耗資源:
????????????????????——當(dāng)多個(gè)視圖(或者說(shuō) CALayer)重疊在一起顯示時(shí),GPU 會(huì)首先把他們混合到一起。如果視圖結(jié)構(gòu)過(guò)于復(fù)雜,混合的過(guò)程也會(huì)消耗很多 GPU 資源。
? ? ? ? ? ? 優(yōu)化方案:????
????????????????????——為了減輕這種情況的 GPU 消耗,應(yīng)用應(yīng)當(dāng)盡量減少視圖數(shù)量和層次,并在不透明的視圖里標(biāo)明 opaque 屬性以避免無(wú)用的 Alpha 通道合成。當(dāng)然,這也可以用上面的方法,把多個(gè)視圖預(yù)先渲染為一張圖片來(lái)顯示。
3、圖片的形成
? ??????????消耗資源:
????????????????????——CALayer 的 border、圓角、陰影、遮罩(mask),CASharpLayer 的矢量圖形顯示,通常會(huì)觸發(fā)離屏渲染(offscreen rendering),而離屏渲染通常發(fā)生在 GPU 中。當(dāng)一個(gè)列表視圖中出現(xiàn)大量圓角的 CALayer,并且快速滑動(dòng)時(shí),可以觀察到 GPU 資源已經(jīng)占滿(mǎn),而 CPU 資源消耗很少。這時(shí)界面仍然能正常滑動(dòng),但平均幀數(shù)會(huì)降到很低。
? ? ? ? ? ? 優(yōu)化方案:????
????????????????????——可以嘗試開(kāi)啟 CALayer.shouldRasterize 屬性,但這會(huì)把原本離屏渲染的操作轉(zhuǎn)嫁到 CPU 上去。對(duì)于只需要圓角的某些場(chǎng)合,也可以用一張已經(jīng)繪制好的圓角圖片覆蓋到原本視圖上面來(lái)模擬相同的視覺(jué)效果。最徹底的解決辦法,就是把需要顯示的圖形在后臺(tái)線(xiàn)程繪制為圖片,避免使用圓角、陰影、遮罩等屬性。
最后作者推薦了個(gè)框架AsyncDisplayKit。該框架對(duì)CALayer進(jìn)行了封裝,當(dāng)使用該框架封裝的view時(shí),里面將消耗性能的操作放在后臺(tái)線(xiàn)程進(jìn)行處理,并且節(jié)省了資源。
但是在平時(shí)日常的開(kāi)發(fā)中,一般是沒(méi)有時(shí)間進(jìn)行優(yōu)化的,最好是將優(yōu)化工作放到后期處理。