[iOS]CPU 資源消耗原因和解決方案[轉(zhuǎn)載]

轉(zhuǎn)自:http://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/

對象創(chuàng)建

對象的創(chuàng)建會分配內(nèi)存匪补、調(diào)整屬性虹统、甚至還有讀取文件等操作,比較消耗 CPU 資源参滴。盡量用輕量的對象代替重量的對象,可以對性能有所優(yōu)化锻弓。比如 CALayer 比 UIView 要輕量許多砾赔,那么不需要響應(yīng)觸摸事件的控件,用 CALayer 顯示會更加合適青灼。如果對象不涉及 UI 操作暴心,則盡量放到后臺線程去創(chuàng)建,但可惜的是包含有 CALayer 的控件杂拨,都只能在主線程創(chuàng)建和操作专普。通過 Storyboard 創(chuàng)建視圖對象時,其資源消耗會比直接通過代碼創(chuàng)建對象要大非常多弹沽,在性能敏感的界面里檀夹,Storyboard 并不是一個好的技術(shù)選擇。
盡量推遲對象創(chuàng)建的時間策橘,并把對象的創(chuàng)建分散到多個任務(wù)中去炸渡。盡管這實現(xiàn)起來比較麻煩,并且?guī)淼膬?yōu)勢并不多丽已,但如果有能力做蚌堵,還是要盡量嘗試一下。如果對象可以復(fù)用,并且復(fù)用的代價比釋放吼畏、創(chuàng)建新對象要小督赤,那么這類對象應(yīng)當(dāng)盡量放到一個緩存池里復(fù)用。

對象調(diào)整

對象的調(diào)整也經(jīng)常是消耗 CPU 資源的地方宫仗。這里特別說一下 CALayer:CALayer 內(nèi)部并沒有屬性够挂,當(dāng)調(diào)用屬性方法時,它內(nèi)部是通過運行時 resolveInstanceMethod 為對象臨時添加一個方法藕夫,并把對應(yīng)屬性值保存到內(nèi)部的一個 Dictionary 里孽糖,同時還會通知 delegate、創(chuàng)建動畫等等毅贮,非常消耗資源办悟。UIView 的關(guān)于顯示相關(guān)的屬性(比如 frame/bounds/transform)等實際上都是 CALayer 屬性映射來的,所以對 UIView 的這些屬性進行調(diào)整時滩褥,消耗的資源要遠大于一般的屬性病蛉。對此你在應(yīng)用中,應(yīng)該盡量減少不必要的屬性修改瑰煎。
當(dāng)視圖層次調(diào)整時铺然,UIView、CALayer 之間會出現(xiàn)很多方法調(diào)用與通知酒甸,所以在優(yōu)化性能時魄健,應(yīng)該盡量避免調(diào)整視圖層次、添加和移除視圖插勤。

對象銷毀

對象的銷毀雖然消耗資源不多沽瘦,但累積起來也是不容忽視的。通常當(dāng)容器類持有大量對象時农尖,其銷毀時的資源消耗就非常明顯析恋。同樣的,如果對象可以放到后臺線程去釋放盛卡,那就挪到后臺線程去助隧。這里有個小 Tip:把對象捕獲到 block 中,然后扔到后臺隊列去隨便發(fā)送個消息以避免編譯器警告滑沧,就可以讓對象在后臺線程銷毀了喇颁。

  • 測試用的代碼(同步釋放時耗時)
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        NSMutableArray *t_list = [[NSMutableArray alloc] init];
        for (int i = 0; i < 9999999 * 3; i++) {
            TestObject *ob = [[TestObject alloc] init];
            [t_list addObject:ob];
        }
        NSDate *s_date = [NSDate date];
        t_list = nil;
        NSDate *e_date = [NSDate date];
        NSLog(@"相差的時間是:%d秒",(int)[e_date timeIntervalSinceDate:s_date]);
    });
  • 修改后的代碼(異步釋放時耗時)
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        NSMutableArray *t_list = [[NSMutableArray alloc] init];
        for (int i = 0; i < 9999999 * 3; i++) {
            TestObject *ob = [[TestObject alloc] init];
            [t_list addObject:ob];
        }
        
        NSMutableArray *m_list = t_list;
        NSDate *s_date = [NSDate date];
        t_list = nil;
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [m_list class];
        });
        
        NSDate *e_date = [NSDate date];
        
        NSLog(@"相差的時間是:%d秒",(int)[e_date timeIntervalSinceDate:s_date]);
    });

布局計算

視圖布局的計算是 App 中最為常見的消耗 CPU 資源的地方。如果能在后臺線程提前計算好視圖布局嚎货、并且對視圖布局進行緩存,那么這個地方基本就不會產(chǎn)生性能問題了蔫浆。
不論通過何種技術(shù)對視圖進行布局殖属,其最終都會落到對 UIView.frame/bounds/center 等屬性的調(diào)整上。上面也說過瓦盛,對這些屬性的調(diào)整非常消耗資源洗显,所以盡量提前計算好布局外潜,在需要時一次性調(diào)整好對應(yīng)屬性,而不要多次挠唆、頻繁的計算和調(diào)整這些屬性处窥。

Autolayout

Autolayout 是蘋果本身提倡的技術(shù),在大部分情況下也能很好的提升開發(fā)效率玄组,但是 Autolayout 對于復(fù)雜視圖來說常常會產(chǎn)生嚴重的性能問題滔驾。隨著視圖數(shù)量的增長,Autolayout 帶來的 CPU 消耗會呈指數(shù)級上升俄讹。具體數(shù)據(jù)可以看這個文章:http://pilky.me/36/哆致。 如果你不想手動調(diào)整 frame 等屬性,你可以用一些工具方法替代(比如常見的 left/right/top/bottom/width/height 快捷屬性)患膛,或者使用 ComponentKit摊阀、AsyncDisplayKit 等框架。

文本計算

如果一個界面中包含大量文本(比如微博微信朋友圈等)踪蹬,文本的寬高計算會占用很大一部分資源胞此,并且不可避免。如果你對文本顯示沒有特殊要求跃捣,可以參考下 UILabel 內(nèi)部的實現(xiàn)方式:用 [NSAttributedString boundingRectWithSize:options:context:] 來計算文本寬高漱牵,用 -[NSAttributedString drawWithRect:options:context:] 來繪制文本。盡管這兩個方法性能不錯枝缔,但仍舊需要放到后臺線程進行以避免阻塞主線程布疙。
如果你用 CoreText 繪制文本,那就可以先生成 CoreText 排版對象愿卸,然后自己計算了灵临,并且 CoreText 對象還能保留以供稍后繪制使用。

文本渲染

屏幕上能看到的所有文本內(nèi)容控件趴荸,包括 UIWebView儒溉,在底層都是通過 CoreText 排版、繪制為 Bitmap 顯示的发钝。常見的文本控件 (UILabel顿涣、UITextView 等),其排版和繪制都是在主線程進行的酝豪,當(dāng)顯示大量文本時涛碑,CPU 的壓力會非常大。對此解決方案只有一個孵淘,那就是自定義文本控件蒲障,用 TextKit 或最底層的 CoreText 對文本異步繪制。盡管這實現(xiàn)起來非常麻煩,但其帶來的優(yōu)勢也非常大揉阎,CoreText 對象創(chuàng)建好后庄撮,能直接獲取文本的寬高等信息,避免了多次計算(調(diào)整 UILabel 大小時算一遍毙籽、UILabel 繪制時內(nèi)部再算一遍)洞斯;CoreText 對象占用內(nèi)存較少,可以緩存下來以備稍后多次渲染坑赡。

圖片的解碼

當(dāng)你用 UIImage 或 CGImageSource 的那幾個方法創(chuàng)建圖片時烙如,圖片數(shù)據(jù)并不會立刻解碼。圖片設(shè)置到 UIImageView 或者 CALayer.contents 中去垮衷,并且 CALayer 被提交到 GPU 前厅翔,CGImage 中的數(shù)據(jù)才會得到解碼。這一步是發(fā)生在主線程的搀突,并且不可避免刀闷。如果想要繞開這個機制,常見的做法是在后臺線程先把圖片繪制到 CGBitmapContext 中仰迁,然后從 Bitmap 直接創(chuàng)建圖片甸昏。目前常見的網(wǎng)絡(luò)圖片庫都自帶這個功能。

圖像的繪制

圖像的繪制通常是指用那些以 CG 開頭的方法把圖像繪制到畫布中徐许,然后從畫布創(chuàng)建圖片并顯示這樣一個過程施蜜。這個最常見的地方就是 [UIView drawRect:] 里面了。由于 CoreGraphic 方法通常都是線程安全的雌隅,所以圖像的繪制可以很容易的放到后臺線程進行翻默。一個簡單異步繪制的過程大致如下(實際情況會比這個復(fù)雜得多,但原理基本一致)

- (void)display {
    dispatch_async(backgroundQueue, ^{
        CGContextRef ctx = CGBitmapContextCreate(...);
        // draw in context...
        CGImageRef img = CGBitmapContextCreateImage(ctx);
        CFRelease(ctx);
        dispatch_async(mainQueue, ^{
            layer.contents = img;
        });
    });
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恰起,一起剝皮案震驚了整個濱河市修械,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌检盼,老刑警劉巖肯污,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吨枉,居然都是意外死亡蹦渣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門貌亭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柬唯,“玉大人,你說我怎么就攤上這事圃庭∪ǘ海” “怎么了美尸?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長斟薇。 經(jīng)常有香客問我,道長恕酸,這世上最難降的妖魔是什么堪滨? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蕊温,結(jié)果婚禮上袱箱,老公的妹妹穿的比我還像新娘。我一直安慰自己义矛,他們只是感情好发笔,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凉翻,像睡著了一般了讨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上制轰,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天前计,我揣著相機與錄音,去河邊找鬼垃杖。 笑死男杈,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的调俘。 我是一名探鬼主播伶棒,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼彩库!你這毒婦竟也來了肤无?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤侧巨,失蹤者是張志新(化名)和其女友劉穎舅锄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體司忱,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡皇忿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坦仍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡繁扎,死狀恐怖幔荒,靈堂內(nèi)的尸體忽然破棺而出糊闽,到底是詐尸還是另有隱情,我是刑警寧澤爹梁,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布右犹,位于F島的核電站,受9級特大地震影響姚垃,放射性物質(zhì)發(fā)生泄漏念链。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一积糯、第九天 我趴在偏房一處隱蔽的房頂上張望掂墓。 院中可真熱鬧,春花似錦看成、人聲如沸君编。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吃嘿。三九已至,卻和暖如春窘游,著一層夾襖步出監(jiān)牢的瞬間唠椭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工忍饰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贪嫂,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓艾蓝,卻偏偏與公主長得像力崇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赢织,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容