iOS 性能優(yōu)化

前言

本文將從發(fā)現(xiàn)問(wèn)題、解決問(wèn)題和預(yù)防問(wèn)題進(jìn)行總結(jié)

如何發(fā)現(xiàn)性能問(wèn)題

  1. 業(yè)務(wù)性能監(jiān)控个束,是指在App本地,業(yè)務(wù)的開(kāi)始和結(jié)束處打點(diǎn)上報(bào)聊疲,然后后臺(tái)統(tǒng)計(jì)達(dá)到監(jiān)控目的茬底;

  2. 卡頓監(jiān)控∈鄱茫卡頓監(jiān)控的實(shí)現(xiàn)一般有兩種方案:

1)主線程卡頓監(jiān)控桩警。通過(guò)子線程監(jiān)測(cè)主線程的 runLoop,判斷兩個(gè)狀態(tài)區(qū)域之間的耗時(shí)是否達(dá)到一定閾值昌妹。具體原理和實(shí)現(xiàn)捶枢,這篇文章介紹得比較詳細(xì)。

(2)FPS監(jiān)控飞崖。要保持流暢的UI交互烂叔,App 刷新率應(yīng)該當(dāng)努力保持在 60fps。監(jiān)控實(shí)現(xiàn)原理比較簡(jiǎn)單固歪,通過(guò)記錄兩次刷新時(shí)間間隔蒜鸡,就可以計(jì)算出當(dāng)前的 FPS。

  • 在實(shí)際應(yīng)用過(guò)程牢裳,無(wú)論是主線程監(jiān)控逢防,還是 FPS 監(jiān)控,抖動(dòng)都比較大蒲讯。因此忘朝,一套綜合的判斷方法,結(jié)合了主線程監(jiān)控判帮、FPS監(jiān)控局嘁,以及CPU使用率等指標(biāo)溉箕,作為判斷卡頓的標(biāo)準(zhǔn)。

性能問(wèn)題的解決方法

  1. 優(yōu)化業(yè)務(wù)流程悦昵。
    性能優(yōu)化看似高深肴茄,真正落到實(shí)處才會(huì)發(fā)現(xiàn),最大的坑往往都隱藏在于業(yè)務(wù)不斷累積和頻繁變更之處但指。
  2. 合理的線程分配
    由于 GCD實(shí)在太方便了寡痰,如果不加控制,大部分需要拋到子線程操作都會(huì)被直接加到 global 隊(duì)列枚赡,這樣會(huì)導(dǎo)致兩個(gè)問(wèn)題:
    (1). 開(kāi)的子線程越來(lái)越多氓癌,線程的開(kāi)銷(xiāo)逐漸明顯谓谦,因?yàn)殚_(kāi)啟線程需要占用一定的內(nèi)存空間(默認(rèn)的情況下贫橙,主線程占1M,子線程占用512KB)。
    (2).多線程情況下反粥,網(wǎng)絡(luò)回調(diào)的時(shí)序問(wèn)題卢肃,導(dǎo)致數(shù)據(jù)處理錯(cuò)亂,而且不容易發(fā)現(xiàn)才顿。為此莫湘,我們項(xiàng)目定了一些基本原則。
  • UI 操作和 DataSource 的操作一定在主線程郑气。
  • DB 操作幅垮、日志記錄、網(wǎng)絡(luò)回調(diào)都在各自的固定線程尾组。
  • 不同業(yè)務(wù)忙芒,可以通過(guò)創(chuàng)建隊(duì)列保證數(shù)據(jù)一致性。

合理的線程分配讳侨,最終目的就是保證主線程盡量少的處理非UI操作呵萨,同時(shí)控制整個(gè)App的子線程數(shù)量在合理的范圍內(nèi)。

  1. 預(yù)處理和延時(shí)加載

預(yù)處理:是將初次顯示需要耗費(fèi)大量線程時(shí)間的操作跨跨,提前放到后臺(tái)線程進(jìn)行計(jì)算潮峦,再將結(jié)果數(shù)據(jù)拿來(lái)顯示。

延時(shí)加載:是指首先加載當(dāng)前必須的可視內(nèi)容勇婴,在稍后一段時(shí)間內(nèi)或特定事件時(shí)忱嘹,再觸發(fā)其他內(nèi)容的加載。這種方式可以很有效的提升界面繪制速度耕渴,使體驗(yàn)更加流暢拘悦。(UITableView 就是最典型的例子)

這兩種方法都是在資源比較緊張的情況下,優(yōu)先處理馬上要用到的數(shù)據(jù)萨螺,同時(shí)盡可能提前加載即將要用到的數(shù)據(jù)窄做。

  1. 緩存
    cache可能是所有性能優(yōu)化中最常用的手段愧驱,但也是我們極不推薦的手段。cache建立的成本低椭盏,見(jiàn)效快组砚,但是帶來(lái)維護(hù)的成本卻很高。如果一定要用掏颊,也請(qǐng)謹(jǐn)慎使用糟红,并注意以下幾點(diǎn):
  • 并發(fā)訪問(wèn) cache 時(shí),數(shù)據(jù)一致性問(wèn)題乌叶。
  • cache 線程安全問(wèn)題盆偿,防止一邊修改一邊遍歷的 crash。
  • cache 查找時(shí)性能問(wèn)題准浴。
  • cache 的釋放與重建事扭,避免占用空間無(wú)限擴(kuò)大,同時(shí)釋放的粒度也要依實(shí)際需求而定乐横。
  1. 使用正確的API
    使用正確的 API求橄,是指在滿足業(yè)務(wù)的同時(shí),能夠選擇性能更優(yōu)的API葡公。
  • 選擇合適的容器;
  • 了解 imageNamed: 與 imageWithContentsOfFile:的差異(imageNamed: 適用于會(huì)重復(fù)加載的小圖片罐农,因?yàn)橄到y(tǒng)會(huì)自動(dòng)緩存加載的圖片,imageWithContentsOfFile: 僅加載圖片)
  • 緩存 NSDateFormatter 的結(jié)果催什。
  • 尋找 (NSDate *)dateFromString:(NSString )string 的替換品涵亏。
//#include <time.h>
time_t t;
struct tm tm;
strptime([iso8601String cStringUsingEncoding:NSUTF8StringEncoding], "%Y-%m-%dT%H:> %M:%S%z", &tm);
tm.tm_isdst = -1;
t = mktime(&tm);
[NSDate dateWithTimeIntervalSince1970:t + [[NSTimeZone localTimeZone] secondsFromGMT]];
  • 不要隨意使用 NSLog().

  • 當(dāng)試圖獲取磁盤(pán)中一個(gè)文件的屬性信息時(shí),使用 [NSFileManager attributesOfItemAtPath:error:] 會(huì)浪費(fèi)大量時(shí)間讀取可能根本不需要的附加屬性蒲凶。這時(shí)可以使用stat代替NSFileManager气筋,直接獲取文件屬性:

#import <sys/stat.h>
struct stat statbuf;
const char *cpath = [filePath fileSystemRepresentation];
if (cpath && stat(cpath, &statbuf) == 0) {
    NSNumber *fileSize = [NSNumber numberWithUnsignedLongLong:statbuf.st_size];
    NSDate *modificationDate = [NSDate dateWithTimeIntervalSince1970:statbuf.st_mtime];
    NSDate *creationDate = [NSDate dateWithTimeIntervalSince1970:statbuf.st_ctime];
    // etc
}

如何預(yù)防性能問(wèn)題

  1. 內(nèi)存泄露檢測(cè)工具
    MLeakFinder是團(tuán)隊(duì)成員zepo在github開(kāi)源的一款內(nèi)存泄露檢測(cè)工具,具體原理和使用方法可以參見(jiàn)這篇文章豹爹。在此之前裆悄,內(nèi)存泄露引起的性能問(wèn)題是很難被察覺(jué)的,只有泄露到了相當(dāng)嚴(yán)重的程度臂聋,然后通過(guò)Instrument工具光稼,不斷嘗試才得以定位。MLeakFinder能在開(kāi)發(fā)階段孩等,把內(nèi)存泄露問(wèn)題暴露無(wú)遺艾君,減少了很多潛在的性能問(wèn)題。

  2. FPS/SQL性能監(jiān)測(cè)工具條
    該工具條是在DEBUG模式下肄方,以浮窗的形式冰垄,實(shí)時(shí)展示當(dāng)前可能存在問(wèn)題的FPS次數(shù)和執(zhí)行時(shí)間較長(zhǎng)的SQL語(yǔ)句個(gè)數(shù),是團(tuán)隊(duì)成員tower的杰作权她。FPS監(jiān)測(cè)的原理并不復(fù)雜虹茶,前文也有介紹逝薪,雖然并不百分百準(zhǔn)確,但非常實(shí)用蝴罪,因?yàn)榭梢噪S時(shí)查看FPS低于某個(gè)閾值時(shí)的堆棧信息董济,再結(jié)合當(dāng)時(shí)的使用場(chǎng)景,開(kāi)發(fā)人員使用起來(lái)非常便利要门,可以很快定位到引起卡頓的場(chǎng)景和原因虏肾。SQL語(yǔ)句的監(jiān)測(cè)也非常實(shí)用.

  3. UI / DataSource主線程檢測(cè)工具。
    該工具是為了保證所有的UI的操作和 DataSource 操作一定是在主線程進(jìn)行欢搜,同樣是由tower同學(xué)貢獻(xiàn)封豪。實(shí)現(xiàn)原理是通過(guò)hook UIView-setNeedsLayout,-setNeedsDisplay炒瘟,-setNeedsDisplayInRect 三個(gè)方法吹埠,確保它們都是在主線程執(zhí)行。子線程操作UI可能會(huì)引起什么問(wèn)題唧领,蘋(píng)果說(shuō)得并不清楚藻雌,實(shí)際開(kāi)發(fā)中我們遇到幾種神奇的問(wèn)題似乎都是跟這個(gè)有關(guān)。

  • app 突然丟動(dòng)畫(huà)斩个,似乎 iOS 系統(tǒng)也有這個(gè) bug。雖然沒(méi)有確切的證據(jù)驯杜,但使用這個(gè)工具受啥,改完所有的問(wèn)題后,bug 也好了(不止一次是這樣)鸽心。

  • UI 操作偶爾響應(yīng)特別慢滚局,從代碼看沒(méi)有任何耗時(shí)操作,只是簡(jiǎn)單的 push 某個(gè) controller顽频。

  • 莫名的 crash藤肢,這當(dāng)然是因?yàn)?UI 操作非線程安全引起的。

  • 更多時(shí)候糯景,子線程操作 UI 也并不一定會(huì)發(fā)生什么問(wèn)題嘁圈,也正因?yàn)椴恢罆?huì)發(fā)生什么,所以更需要我們警惕蟀淮,這個(gè)工具替我們掃除了這些隱患最住。雖然,蘋(píng)果表示怠惶,現(xiàn)在部分的 UI 操作也已經(jīng)是線程安全了涨缚,但畢竟大部分還不是。DataSource 的監(jiān)測(cè)是因?yàn)槲覀儤I(yè)務(wù)定下的原則策治,保證列表 DataSource 的線程安全脓魏。

卡頓產(chǎn)生的原因

圖片.png

界面卡頓的原因:在 VSync 信號(hào)到來(lái)后兰吟,系統(tǒng)圖形服務(wù)會(huì)通過(guò) CADisplayLink 等機(jī)制通知 App,App 主線程開(kāi)始在CPU中計(jì)算顯示內(nèi)容茂翔,比如視圖的創(chuàng)建揽祥、布局計(jì)算、圖片解碼檩电、文本繪制等拄丰。隨后 CPU 會(huì)將計(jì)算好的內(nèi)容提交到 GPU去,由 GPU 進(jìn)行變換俐末、合成料按、渲染。隨后 GPU 會(huì)把渲染結(jié)果提交到幀緩沖區(qū)去卓箫,等待下一次VSync信號(hào)到來(lái)時(shí)顯示到屏幕上载矿。由于垂直同步的機(jī)制,如果在一個(gè) VSync 時(shí)間內(nèi)烹卒,CPU 或者 GPU 沒(méi)有完成內(nèi)容提交闷盔,則那一幀就會(huì)被丟棄,等待下一次機(jī)會(huì)再顯示旅急,而這時(shí)顯示屏?xí)A糁暗膬?nèi)容不變逢勾。
在開(kāi)發(fā)中,CPU和GPU中任何一個(gè)壓力過(guò)大藐吮,都會(huì)導(dǎo)致掉幀現(xiàn)象溺拱,所以在開(kāi)發(fā)時(shí),也需要分別對(duì)CPU和GPU壓力進(jìn)行評(píng)估和優(yōu)化谣辞。

CPU:加載資源迫摔,對(duì)象創(chuàng)建,對(duì)象調(diào)整泥从,對(duì)象銷(xiāo)毀句占,布局計(jì)算,Autolayout躯嫉,文本計(jì)算纱烘,文本渲染,圖片的解碼和敬, 圖像的繪制(Core Graphics)都是在CPU上面進(jìn)行的凹炸。

GPU:GPU是一個(gè)專門(mén)為圖形高并發(fā)計(jì)算而量身定做的處理單元,比CPU使用更少的電來(lái)完成工作并且GPU的浮點(diǎn)計(jì)算能力要超出CPU很多昼弟。
GPU的渲染性能要比CPU高效很多啤它,同時(shí)對(duì)系統(tǒng)的負(fù)載和消耗也更低一些,所以在開(kāi)發(fā)中,我們應(yīng)該盡量讓CPU負(fù)責(zé)主線程的UI調(diào)動(dòng)变骡,把圖形顯示相關(guān)的工作交給GPU來(lái)處理离赫,當(dāng)涉及到光柵化等一些工作時(shí),CPU也會(huì)參與進(jìn)來(lái)塌碌,這點(diǎn)在后面再詳細(xì)描述渊胸。
相對(duì)于CPU來(lái)說(shuō),GPU能干的事情比較單一:接收提交的紋理(Texture)和頂點(diǎn)描述(三角形)台妆,應(yīng)用變換(transform)翎猛、混合(合成)并渲染,然后輸出到屏幕上接剩。通常你所能看到的內(nèi)容切厘,主要也就是紋理(圖片)和形狀(三角模擬的矢量圖形)兩類(lèi)。

CPU 和 GPU 的協(xié)作:要在屏幕上顯示視圖懊缺,需要CPU和GPU一起協(xié)作疫稿,CPU計(jì)算好顯示的內(nèi)容提交到GPU,GPU渲染完成后將結(jié)果放到幀緩存區(qū)鹃两,隨后視頻控制器會(huì)按照 VSync 信號(hào)逐行讀取幀緩沖區(qū)的數(shù)據(jù)遗座,經(jīng)過(guò)可能的數(shù)模轉(zhuǎn)換傳遞給顯示器顯示。

緩沖機(jī)制:iOS使用的是雙緩沖機(jī)制俊扳。即GPU會(huì)預(yù)先渲染好一幀放入一個(gè)緩沖區(qū)內(nèi)(前幀緩存)途蒋,讓視頻控制器讀取,當(dāng)下一幀渲染好后拣度,GPU會(huì)直接把視頻控制器的指針指向第二個(gè)緩沖器(后幀緩存)碎绎。當(dāng)你視頻控制器已經(jīng)讀完一幀,準(zhǔn)備讀下一幀的時(shí)候抗果,GPU會(huì)等待顯示器的VSync信號(hào)發(fā)出后,前幀緩存和后幀緩存會(huì)瞬間切換奸晴,后幀緩存會(huì)變成新的前幀緩存冤馏,同時(shí)舊的前幀緩存會(huì)變成新的后幀緩存。

iOS 保持界面流暢的技巧
LLDebugTool是一款針對(duì)開(kāi)發(fā)者和測(cè)試者的調(diào)試工具
iOS 性能優(yōu)化總結(jié)
iOS性能優(yōu)化系列篇之“優(yōu)化總體原則”
iOS應(yīng)用UI線程卡頓監(jiān)控
UITableView-FDTemplateLayoutCell - 緩存

繪制像素到屏幕上
iOS圖形原理與離屏渲染
iOS 保持界面流暢的技巧
Advanced Graphics and Animations for iOS Apps(session 419)

使用 ASDK 性能調(diào)優(yōu) - 提升 iOS 界面的渲染性能
Designing for iOS: Graphics & Performance
iOS離屏渲染之優(yōu)化分析
iOS視圖渲染以及性能優(yōu)化總結(jié)
iOS 離屏渲染
深刻理解移動(dòng)端優(yōu)化之離屏渲染
iOS 流暢度性能優(yōu)化寄啼、CPU逮光、GPU、離屏渲染
iOS 圖形性能優(yōu)化錦集
離屏渲染優(yōu)化詳解:實(shí)例示范+性能測(cè)試

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末墩划,一起剝皮案震驚了整個(gè)濱河市涕刚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乙帮,老刑警劉巖杜漠,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡驾茴,警方通過(guò)查閱死者的電腦和手機(jī)盼樟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)锈至,“玉大人晨缴,你說(shuō)我怎么就攤上這事∠考瘢” “怎么了击碗?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)们拙。 經(jīng)常有香客問(wèn)我稍途,道長(zhǎng),這世上最難降的妖魔是什么睛竣? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任晰房,我火速辦了婚禮,結(jié)果婚禮上射沟,老公的妹妹穿的比我還像新娘殊者。我一直安慰自己,他們只是感情好验夯,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布猖吴。 她就那樣靜靜地躺著,像睡著了一般挥转。 火紅的嫁衣襯著肌膚如雪海蔽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天绑谣,我揣著相機(jī)與錄音党窜,去河邊找鬼。 笑死借宵,一個(gè)胖子當(dāng)著我的面吹牛幌衣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播壤玫,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼豁护,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了欲间?” 一聲冷哼從身側(cè)響起楚里,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎猎贴,沒(méi)想到半個(gè)月后班缎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蝴光,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年吝梅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虱疏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡苏携,死狀恐怖做瞪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情右冻,我是刑警寧澤装蓬,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站纱扭,受9級(jí)特大地震影響牍帚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乳蛾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一暗赶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肃叶,春花似錦蹂随、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蹦魔,卻和暖如春激率,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背勿决。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工乒躺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人低缩。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓聪蘸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親表制。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345