轉載:iOS構建流暢的交互界面--CPU,GPU資源消耗的原因和解決方案

原文: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上去。
  • 圓角圖片遮擋
  • 把需要顯示的圖形在后臺線程繪制為圖片,避免使用圓角,陰影,遮罩等屬性。
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容