原文:https://www.cnblogs.com/HackHer/p/6351460.html
** CPU資源消耗的原因和解決方案**
對象創(chuàng)建
輕量對象代替重量對象
- 不需要響應觸摸事件的控件:CALayer顯示
- 對象不涉及UI操作,則盡量放到后臺線程創(chuàng)建
- 包含有CALayer的控件只能在主線程創(chuàng)建和操作
- 通過Storyboard 創(chuàng)建視圖對象時,其資源消耗會比直接通過代碼創(chuàng)建對象要大非常多,在性能敏感的界面里,storyboard不是一個好的技術選擇
- 盡量推遲對象創(chuàng)建的時間,并把對象的創(chuàng)建分散到多個任務中去。
- 對象的復用代價比釋放,創(chuàng)建新對象要小,這類對象應當盡量放到一個緩存池里復用
對象調整 - CALayer:CALayer內(nèi)部并沒有屬性,當調用屬性方法時,它內(nèi)部是通過運行時resolveInstanceMethod為對象臨時添加一個方法,并把對應屬性值保存到內(nèi)部的一個Dictionary里,同時還會通知delegate,創(chuàng)建動畫等等,非常消耗資源。
- UIView的關于顯示相關的屬性(frame/bound/transform)等實際上都是CALayer屬性映射來的,所以對UIView的這些屬性進行調整時,消耗的資源要遠大于一般的屬性,所以,盡量減少不必要的屬性修改
- 當視圖層次調整時,UIView,CALayer之間會出現(xiàn)很多方法調用與通知,所以,應盡量避免調整視圖層次,添加和移除視圖。
對象銷毀
把對象捕獲到block中,然后扔到后臺隊列去隨便發(fā)送個消息以避免編譯器警告,就可以讓對象在后臺線程銷毀了。
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">NSArray *tmp = self.array;
self.array = nil;
dispatch_async(queue,^{
[tmp class];
});</pre>
布局計算
- 視圖布局的計算是App中最為常見的消耗CPU資源的地方
- 在后臺線程提前計算好視圖布局,并且對視圖布局進行緩存
- 用任何技術對視圖進行布局,最終都會落到對UIView.frame/bounds/center等屬性的調整上 對象調整:非常消耗資源,所以盡量提前計算好布局,在需要時一次性調整好對應屬性,而不要多次,頻繁的計算和調整這些屬性。
Autolayout
不手動調整frame 等屬性,可以用常見的快捷屬性:left/right/top/bottom/width/height,或使用ComponentKit,AsyncDisplayKit等框架
文本渲染
- 所有的文本內(nèi)容控件,在底層都是通過CoreText排版,繪制為Bitmap顯示的。
- 常見的文本控件(UILabel,UITextView),其排版和繪制都是在主線程進行的,當顯示大量文本時,CPU的壓力會非常大。
- 解決方案:自定義文本控件,用TextKit或底層的CoreText對文本異步繪制
- CoreText對象創(chuàng)建好后,能直接獲取文本的寬高信息,避免了多次計算(調整UILabel大小時算一遍,UILabel繪制時內(nèi)部再算一遍),CoreText對象占用內(nèi)存較少,可以緩存下來供稍后多次渲染。
圖片的解碼
- 用UIImage 或CGImageSource創(chuàng)建圖片時,圖片數(shù)據(jù)不會立刻解碼。圖片設置到UIImageView或者CALayer.contents中去,并且CALayer被提交到GPU前,CGImage中的數(shù)據(jù)才會得到解碼。 發(fā)生在主線程,不可避免。
- 后臺線程先把圖片會知道CGBitmapContext中,然后從Bitmap直接創(chuàng)建圖片。
圖像的繪制
** 圖像繪制:以CG開頭的方法把圖像繪制到畫布中,然后從畫布創(chuàng)建圖片并顯示 如:[UIView drawRect:]
- CoreGraphic 方法通常是線程安全的,圖像的繪制可以放到后臺線程進行*

](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">- (void)display{
dispatch_async(backgroundQueue,^{
CGContextRef ctx = CGBitmapContextCreate(...); // draw in context...
CGImageRef img = CGBitmapContextCreateImage(ctx);
CFRelease(ctx);
dispatch_async(mainQueue,^{
layer.contents = img;
});
});
} </pre>

](javascript:void(0); "復制代碼")
GPU 資源消耗原因和解決方案
GPU:接收提交的紋理和頂點描述(三角形),應用變換(transform),混合并渲染,然后輸出到屏幕上。
所看到的內(nèi)容:紋理和形狀(三角形模擬的矢量圖形)
紋理的渲染
- 所有的Bitmap ,包括圖片,文本,柵格化的內(nèi)容,最終都要由內(nèi)存提交到顯存,綁定為GPU Texture.
- 提交到顯存的過程,GPU調整和渲染Texture的過程,都要消耗不少GPU資源
- 當在較短時間顯示大量圖片(TableView存在非常多的圖片并且快速滑動時),CPU占有率很低,GPU占有非常高,界面仍然會掉幀。
- 盡量減少在短時間內(nèi)大量圖片的顯示,盡可能將多張圖片合成為一張進行顯示。
- 圖片過大,超過GPU的最大紋理尺寸時,圖片需要先由CPU進行預處理,這對CPU和GPU都會帶來額外的資源消耗。iPhone4S以上機型,紋理尺寸上限4096*4096
視圖的混合
- 當多個視圖(CALayer)重疊在一起顯示時,GPU會首先把他們混合到一起。如果視圖結構過于復雜,混合的過程也會消耗很多GPU資源。
- 應用應當盡量減少視圖數(shù)量和層次,并在不透明的視圖里標明opaque屬性以避免無用的Alpha通道合成。
- 把多個視圖預先渲染為一張圖片來顯示。
圖形的生成
- CALayer的border,圓角,陰影,遮罩(mask),CASharpLayer的矢量圖形顯示,通常會觸發(fā)離屏渲染(offscreen rendering),而離屏渲染通暢發(fā)生在GPU中。
- 當一個列表視圖中出現(xiàn)大量圓角的CALayer,并且快速滑動時,可以觀察到GPU資源已經(jīng)占滿,而CPU資源消耗很少。界面仍然能正?;瑒?,但平均幀數(shù)會降到很低
- 避免這種情況,可以嘗試開啟CALayer.shouldRasterize(柵格化)屬性,但這會把原本離屏渲染的操作轉嫁到CPU上去。
- 圓角圖片遮擋
- 把需要顯示的圖形在后臺線程繪制為圖片,避免使用圓角,陰影,遮罩等屬性。