iOS之武功秘籍?: 界面優(yōu)化

iOS之武功秘籍 文章匯總

寫在前面

我們經(jīng)常在面試中,會被問及關(guān)于界面優(yōu)化相關(guān)的問題,比如為什么界面會出現(xiàn)卡頓?如何監(jiān)控卡頓?接著如何解決卡頓?那么本篇文章將重點(diǎn)分析一下卡頓的原理和解決的措施.

本節(jié)可能用到的秘籍Demo

一、界面卡頓

通常來說,計(jì)算機(jī)中的顯示過程是下面這樣的,通過CPUGPU顯示器協(xié)同工作來將圖片顯示到屏幕上

  • 1、CPU計(jì)算好顯示內(nèi)容,提交至GPU
  • 2、GPU經(jīng)過渲染完成后將渲染的結(jié)果放入FrameBuffer(幀緩存區(qū))
  • 3、隨后視頻控制器會按照VSync信號逐行讀取FrameBuffer的數(shù)據(jù)
  • 4、經(jīng)過可能的數(shù)模轉(zhuǎn)換傳遞給顯示器進(jìn)行顯示

最開始時(shí),FrameBuffer只有一個(gè),這種情況下FrameBuffer的讀取和刷新有很大的效率問題,為了解決這個(gè)問題,引入了雙緩存區(qū).即雙緩沖機(jī)制.在這種情況下,GPU會預(yù)先渲染好一幀放入FrameBuffer,讓視頻控制器讀取,當(dāng)下一幀渲染好后,GPU會直接將視頻控制器的指針指向第二個(gè)FrameBuffer.

雙緩存機(jī)制雖然解決了效率問題,但是隨之而言的是新的問題,當(dāng)視頻控制器還未讀取完成時(shí),例如屏幕內(nèi)容剛顯示一半,GPU將新的一幀內(nèi)容提交到FrameBuffer,并將兩個(gè)FrameBuffer進(jìn)行交換后,視頻控制器就會將新的一幀數(shù)據(jù)的下半段顯示到屏幕上,造成屏幕撕裂現(xiàn)象.

為了解決這個(gè)問題,采用了垂直同步信號機(jī)制.當(dāng)開啟垂直同步后,GPU會等待顯示器的VSync信號發(fā)出后,才進(jìn)行新的一幀渲染和FrameBuffer更新.而目前iOS設(shè)備中采用的正是雙緩存區(qū)+VSync.

屏幕卡頓原因

下面我們來說說,屏幕卡頓的原因.

VSync信號到來后,系統(tǒng)圖形服務(wù)會通過 CADisplayLink 等機(jī)制通知 AppApp 主線程開始在CPU中計(jì)算顯示內(nèi)容.隨后 CPU 會將計(jì)算好的內(nèi)容提交到 GPU 去,由GPU進(jìn)行變換、合成、渲染.隨后 GPU 會把渲染結(jié)果提交到幀緩沖區(qū)去,等待下一次 VSync 信號到來時(shí)顯示到屏幕上.由于垂直同步的機(jī)制,如果在一個(gè) VSync 時(shí)間內(nèi),CPU 或者 GPU 沒有完成內(nèi)容提交,則那一幀就會被丟棄,等待下一次機(jī)會再顯示,而這時(shí)顯示屏?xí)A糁暗膬?nèi)容不變.所以可以簡單理解掉幀過時(shí)不候.

如下圖所示,是一個(gè)顯示過程,第1幀在VSync到來前,處理完成,正常顯示,第2幀在VSync到來后,仍在處理中,此時(shí)屏幕不刷新,依舊顯示第1幀,此時(shí)就出現(xiàn)了掉幀情況,渲染時(shí)就會出現(xiàn)明顯的卡頓現(xiàn)象.

從圖中可以看出,CPUGPU不論是哪個(gè)阻礙了顯示流程,都會造成掉幀現(xiàn)象,所以為了給用戶提供更好的體驗(yàn),在開發(fā)中,我們需要進(jìn)行卡頓檢測以及相應(yīng)的優(yōu)化.

二、卡頓監(jiān)控

卡頓監(jiān)控的方案一般有兩種:

  • FPS監(jiān)控:為了保持流暢的UI交互,App的刷新拼搏應(yīng)該保持在60fps左右,其原因是因?yàn)?code>iOS設(shè)備默認(rèn)的刷新頻率是60次/秒,而1次刷新(即VSync信號發(fā)出)的間隔是 1000ms/60 = 16.67ms,所以如果在16.67ms內(nèi)沒有準(zhǔn)備好下一幀數(shù)據(jù),就會產(chǎn)生卡頓

  • 主線程卡頓監(jiān)控:通過子線程監(jiān)測主線程的RunLoop,判斷兩個(gè)狀態(tài)(kCFRunLoopBeforeSourceskCFRunLoopAfterWaiting)之間的耗時(shí)是否達(dá)到一定閾值

FPS監(jiān)控

FPS的監(jiān)控,參照YYKit中的YYFPSLabel,主要是通過CADisplayLink實(shí)現(xiàn).借助link的時(shí)間差,來計(jì)算一次刷新所需的時(shí)間,然后通過 刷新次數(shù) / 時(shí)間差 得到刷新頻次,并判斷是否其范圍,通過顯示不同的文字顏色來表示卡頓嚴(yán)重程度.如果只是簡單的監(jiān)測,使用FPS足夠了.

主線程卡頓監(jiān)控

除了FPS,還可以通過RunLoop來監(jiān)控,因?yàn)榭D的是事務(wù),而事務(wù)是交由主線程RunLoop處理的.

實(shí)現(xiàn)思路:檢測主線程每次執(zhí)行消息循環(huán)的時(shí)間,當(dāng)這個(gè)時(shí)間大于規(guī)定的閾值時(shí),就記為發(fā)生了一次卡頓.這個(gè)也是微信卡頓三方matrix的原理.

卡頓檢測三方庫:

  • Swift的卡頓檢測第三方ANREye,其主要思路是:創(chuàng)建子線程進(jìn)行循環(huán)監(jiān)測,每次檢測時(shí)設(shè)置標(biāo)記置為true,然后派發(fā)任務(wù)到主線程,標(biāo)記置為false,接著子線程睡眠超過閾值時(shí),判斷標(biāo)記是否為false,如果沒有,說明主線程發(fā)生了卡頓

  • OC可以使用 微信matrix滴滴DoraemonKit

三、界面優(yōu)化

CPU層面的優(yōu)化

  • 1、盡量用輕量級的對象代替重量級的對象,可以對性能有所優(yōu)化,例如 不需要相應(yīng)觸摸事件的控件,用CALayer代替UIView

  • 2、盡量減少對UIViewCALayer的屬性修改

    • CALayer內(nèi)部并沒有屬性,當(dāng)調(diào)用屬性方法時(shí),其內(nèi)部是通過運(yùn)行時(shí)resolveInstanceMethod為對象臨時(shí)添加一個(gè)方法,并將對應(yīng)屬性值保存在內(nèi)部的一個(gè)Dictionary中,同時(shí)還會通知delegate、創(chuàng)建動畫等,非常耗時(shí)

    • UIView相關(guān)的顯示屬性,例如frameboundstransform等,實(shí)際上都是從CALayer映射來的,對其進(jìn)行調(diào)整時(shí),消耗的資源比一般屬性要大

  • 3、當(dāng)有大量對象釋放時(shí),也是非常耗時(shí)的,盡量挪到后臺線程去釋放

  • 4、盡量提前計(jì)算視圖布局,即預(yù)排版,例如cell的行高

  • 5、Autolayout在簡單頁面情況下們可以很好的提升開發(fā)效率,但是對于復(fù)雜視圖而言,會產(chǎn)生嚴(yán)重的性能問題,隨著視圖數(shù)量的增長,Autolayout帶來的CPU消耗是呈指數(shù)上升的.所以盡量使用代碼布局.如果不想手動調(diào)整frame等,也可以借助三方庫,例如Masonry(OC)、SnapKit(Swift)、ComponentKit、AsyncDisplayKit等

  • 6、文本處理的優(yōu)化:當(dāng)一個(gè)界面有大量文本時(shí),其行高的計(jì)算、繪制也是非常耗時(shí)的

    • 1)如果對文本沒有特殊要求,可以使用UILabel內(nèi)部的實(shí)現(xiàn)方式,且需要放到子線程中進(jìn)行,避免阻塞主線程

      • 計(jì)算文本寬高:[NSAttributedString boundingRectWithSize:options:context:]
      • 文本繪制:[NSAttributedString drawWithRect:options:context:]
    • 2)自定義文本控件,利用TextKit 或最底層的 CoreText 對文本異步繪制.并且CoreText 對象創(chuàng)建好后,能直接獲取文本的寬高等信息,避免了多次計(jì)算(調(diào)整和繪制都需要計(jì)算一次).CoreText直接使用了CoreGraphics占用內(nèi)存小,效率高

  • 7、圖片處理(解碼 + 繪制)

    • 當(dāng)使用UIImageCGImageSource 的方法創(chuàng)建圖片時(shí),圖片的數(shù)據(jù)不會立即解碼,而是在設(shè)置時(shí)解碼(即圖片設(shè)置到UIImageView/CALayer.contents中,然后在CALayer提交至GPU渲染前,CGImage中的數(shù)據(jù)才進(jìn)行解碼).這一步是無可避免的,且是發(fā)生在主線程中的.想要繞開這個(gè)機(jī)制,常見的做法是在子線程中先將圖片繪制到CGBitmapContext,然后從Bitmap 直接創(chuàng)建圖片,例如SDWebImage三方框架中對圖片編解碼的處理。這就是Image的預(yù)解碼

    • 當(dāng)使用CG開頭的方法繪制圖像到畫布中,然后從畫布中創(chuàng)建圖片時(shí),可以將圖像的繪制在子線程中進(jìn)行

  • 8、圖片優(yōu)化

    • 盡量使用PNG圖片,不使用JPGE圖片
    • 通過子線程預(yù)解碼,主線程渲染,即通過Bitmap創(chuàng)建圖片,在子線程賦值image
    • 優(yōu)化圖片大小,盡量避免動態(tài)縮放
    • 盡量將多張圖合為一張進(jìn)行顯示
  • 9、盡量避免使用透明view,因?yàn)槭褂猛该?code>view,會導(dǎo)致在GPU中計(jì)算像素時(shí),會將透明view下層圖層的像素也計(jì)算進(jìn)來,即顏色混合處理

  • 10、按需加載,例如在TableView中滑動時(shí)不加載圖片,使用默認(rèn)占位圖,而是在滑動停止時(shí)加載

  • 11、少使用addViewcell動態(tài)添加view

GPU層面優(yōu)化

相對于CPU而言,GPU主要是接收CPU提交的紋理+頂點(diǎn),經(jīng)過一系列transform,最終混合并渲染,輸出到屏幕上.

  • 1、盡量減少在短時(shí)間內(nèi)大量圖片的顯示,盡可能將多張圖片合為一張顯示,主要是因?yàn)楫?dāng)有大量圖片進(jìn)行顯示時(shí),無論是CPU的計(jì)算還是GPU的渲染,都是非常耗時(shí)的,很可能出現(xiàn)掉幀的情況

  • 2、盡量避免圖片的尺寸超過4096×4096,因?yàn)楫?dāng)圖片超過這個(gè)尺寸時(shí),會先由CPU進(jìn)行預(yù)處理,然后再提交給GPU處理,導(dǎo)致額外CPU資源消耗

  • 3、盡量減少視圖數(shù)量和層次,主要是因?yàn)橐晥D過多且重疊時(shí),GPU會將其混合,混合的過程也是非常耗時(shí)的

  • 4、盡量避免離屏渲染

  • 5、異步渲染,例如可以將cell中的所有控件、視圖合成一張圖片進(jìn)行顯示.可以參考Graver三方框架

提示

上述這些優(yōu)化方式的落地實(shí)現(xiàn),需要根據(jù)自身項(xiàng)目進(jìn)行評估,合理的使用進(jìn)行優(yōu)化

寫在后面

和諧學(xué)習(xí),不急不躁.我還是我,顏色不一樣的煙火.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,677評論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,643評論 2 380

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