App 啟動速度怎么做優(yōu)化與監(jiān)控


愛情就是死循環(huán),一旦執(zhí)行就陷進(jìn)去了喉童;愛上一個人撇寞,就是內(nèi)存泄漏–你永遠(yuǎn)釋放不了;真正愛上一個人的時候堂氯,那就是常量限定重抖,永遠(yuǎn)不會改變;女朋友就是私有變量祖灰,只有我這個類才能調(diào)用钟沛;情人就是指針用的時候一定要注意,要不然就帶來巨大的災(zāi)難 -局扶,-

分享來自戴銘的文章恨统,干貨滿滿


App 啟動時都干了些什么事兒叁扫?

一般而言,App 的啟動時間畜埋,指的是從用戶點擊 App 開始莫绣,到用戶看到第一個界面之間的時間∮瓢埃總結(jié)來說对室,App 的啟動主要包括三個階段:

  1. main() 函數(shù)執(zhí)行前;
  2. main() 函數(shù)執(zhí)行后咖祭;
  3. 首屏渲染完成后掩宜。

main() 函數(shù)執(zhí)行前

在 main() 函數(shù)執(zhí)行前,系統(tǒng)主要會做下面幾件事情:

  • 加載可執(zhí)行文件(App 的.o 文件的集合)么翰;
    加載動態(tài)鏈接庫牺汤,進(jìn)行 rebase 指針調(diào)整和 bind 符號綁定;
  • Objc 運(yùn)行時的初始處理浩嫌,包括 Objc 相關(guān)類的注冊檐迟、category 注冊、selector 唯一性
    檢查等码耐;
  • 初始化追迟,包括了執(zhí)行 +load() 方法、attribute((constructor)) 修飾的函數(shù)的調(diào)用骚腥、創(chuàng)建
  • C++ 靜態(tài)全局變量怔匣。

相應(yīng)地,這個階段對于啟動速度優(yōu)化來說桦沉,可以做的事情包括:

減少動態(tài)庫加載

每個庫本身都有依賴關(guān)系,蘋果公司建議使用更少的動態(tài)庫金闽,并且建議在使用動態(tài)庫的數(shù)量較多時纯露,盡量將多個動態(tài)庫進(jìn)行合并。數(shù)量上代芜,蘋果公司最多可以支持 6 個非系統(tǒng)動態(tài)庫合并為一個埠褪。

減少加載啟動后不會去使用的類或者方法。

+load() 方法里的內(nèi)容可以放到首屏渲染完成后再執(zhí)行挤庇,或使用 +initialize() 方法替換掉钞速。因為,在一個 +load() 方法里嫡秕,進(jìn)行運(yùn)行時方法替換操作會帶來 4 毫秒的消耗渴语。不要小看這 4 毫秒,積少成多昆咽,執(zhí)行 +load() 方法對啟動速度的影響會越來越大驾凶。

控制 C++ 全局變量的數(shù)量牙甫。

main() 函數(shù)執(zhí)行后

main() 函數(shù)執(zhí)行后的階段,指的是從 main() 函數(shù)執(zhí)行開始调违,到 appDelegate 的didFinishLaunchingWithOptions 方法里首屏渲染相關(guān)方法執(zhí)行完成窟哺。
首頁的業(yè)務(wù)代碼都是要在這個階段,也就是首屏渲染前執(zhí)行的技肩,主要包括了:

  • 首屏初始化所需配置文件的讀寫操作且轨;

  • 首屏列表大數(shù)據(jù)的讀取虚婿;

  • 首屏渲染的大量計算等

很多時候旋奢,開發(fā)者會把各種初始化工作都放到這個階段執(zhí)行,導(dǎo)致渲染完成滯后雳锋。更加優(yōu)化的開發(fā)方式黄绩,應(yīng)該是從功能上梳理出哪些是首屏渲染必要的初始化功能,哪些是 App 啟動必要的初始化功能玷过,而哪些是只需要在對應(yīng)功能開始使用時才需要初始化的爽丹。梳理完之后,將這些初始化功能分別放到合適的階段進(jìn)行

首屏渲染完成后

首屏渲染后的這個階段辛蚊,主要完成的是粤蝎,非首屏其他業(yè)務(wù)服務(wù)模塊的初始化、監(jiān)聽的注冊袋马、配置文件的讀取等初澎。從函數(shù)上來看,這個階段指的就是截止到didFinishLaunchingWithOptions方法作用域內(nèi)執(zhí)行首屏渲染之后的所有方法執(zhí)行完成虑凛。
簡單說的話碑宴,這個階段就是從渲染完成時開始,到 didFinishLaunchingWithOptions 方法作用域結(jié)束時結(jié)束桑谍。

這個階段用戶已經(jīng)能夠看到 App 的首頁信息了延柠,所以優(yōu)化的優(yōu)先級排在最后。但是锣披,那些會卡住主線程的方法還是需要最優(yōu)先處理的贞间,不然還是會影響到用戶后面的交互操作。
明白了 App 啟動階段需要完成的工作后雹仿,我們就可以有的放矢地進(jìn)行啟動速度的優(yōu)化了增热。
這些優(yōu)化,包括了功能級別和方法級別的啟動優(yōu)化胧辽。接下來峻仇,我們就從這兩個角度展開看看。

功能級別的啟動優(yōu)化

我想邑商,你所在的團(tuán)隊一定面臨過啟動階段的代碼功能堆積础浮、無規(guī)范帆调、難維護(hù)的問題吧。在App 項目開發(fā)初期豆同,開發(fā)人員不多番刊、代碼量也沒那么大時,這種情況比較少見影锈。但到了后期 芹务,App 業(yè)務(wù)規(guī)模擴(kuò)大,團(tuán)隊人員水平參差不齊鸭廷,各種代碼問題就會爆發(fā)出來枣抱,終歸需要來次全面治理。

而全面治理過程中的手段辆床、方法和碰到的問題佳晶,對于后面的規(guī)范制定以及啟動速度監(jiān)控都有著重要的意義。那么讼载,我們要怎樣從功能級別來進(jìn)行全面的啟動優(yōu)化治理呢轿秧?功能級別的啟動優(yōu)化,就是要從 main() 函數(shù)執(zhí)行后這個階段下手咨堤。

優(yōu)化的思路是: main() 函數(shù)開始執(zhí)行后到首屏渲染完成前只處理首屏相關(guān)的業(yè)務(wù)菇篡,其他非首屏業(yè)務(wù)的初始化、監(jiān)聽注冊一喘、配置文件讀取等都放到首屏渲染完成后去做驱还。

方法級別的啟動優(yōu)化

經(jīng)過功能級別的啟動優(yōu)化,也就是將非首屏業(yè)務(wù)所需的功能滯后以后凸克,從用戶點擊 App 到
看到首屏的時間將會有很大程度的縮短议蟆,也就達(dá)到了優(yōu)化 App 啟動速度的目的。
在這之后萎战,我們需要進(jìn)一步做的咐容,是檢查首屏渲染完成前主線程上有哪些耗時方法,將沒必要的耗時方法滯后或者異步執(zhí)行撞鹉。通常情況下,耗時較長的方法主要發(fā)生在計算大量數(shù)據(jù)的情況下颖侄,具體的表現(xiàn)就是加載鸟雏、編輯、存儲圖片和文件等資源览祖。

那么孝鹊,你覺得是不是只需要優(yōu)化對資源的操作就可以了呢?

當(dāng)然不是展蒂。就像 +load() 方法又活,一個耗時 4 毫秒苔咪,100 個就是 400 毫秒,這種耗時用戶也是能明顯感知到的柳骄。比如团赏,我以前使用的 ReactiveCocoa 框架(這是一個 iOS 上的響應(yīng)式編程框架),每創(chuàng)建一個信號都有 6 毫秒的耗時耐薯。這樣舔清,稍不注意各種信號的創(chuàng)建就都被放在了首屏渲染完成前,進(jìn)而導(dǎo)致 App 的啟動速度大幅變慢曲初。類似這樣單個方法耗時不多体谒,但是由于堆積導(dǎo)致 App 啟動速度大幅變慢的方法數(shù)不勝數(shù)。
所以臼婆,你需要一個能夠?qū)臃椒ê臅r進(jìn)行全面抒痒、精確檢查的手段。
那么問題來了颁褂,有哪些監(jiān)控手段故响?這些監(jiān)控手段各有什么優(yōu)缺點?你又該如何選擇呢痢虹?

目前來看被去,對 App 啟動速度的監(jiān)控,主要有兩種手段奖唯。

第一種方法是惨缆,定時抓取主線程上的方法調(diào)用堆棧,計算一段時間里各個方法的耗時丰捷。

Xcode 工具套件里自帶的 Time Profiler 坯墨,采用的就是這種方式。這種方式的優(yōu)點是病往,開發(fā)類似工具成本不高捣染,能夠快速開發(fā)后集成到你的 App 中,以便在真實環(huán)境中進(jìn)行檢查停巷。說到定時抓取耍攘,就會涉及到定時間隔的長短問題。

定時間隔設(shè)置得長了畔勤,會漏掉一些方法蕾各,從而導(dǎo)致檢查出來的耗時不精確;而定時間隔設(shè)置得短了庆揪,抓取堆棧這個方法本身調(diào)用過多也會影響整體耗時式曲,導(dǎo)致結(jié)果不準(zhǔn)確。

這個定時間隔如果小于所有方法執(zhí)行的時間(比如 0.002 秒),那么基本就能監(jiān)控到所有方法吝羞。但這樣做的話兰伤,整體的耗時時間就不夠準(zhǔn)確。一般將這個定時間隔設(shè)置為 0.01 秒钧排。這樣設(shè)置敦腔,對整體耗時的影響小,不過很多方法耗時就不精確了卖氨。但因為整體耗時的數(shù)據(jù)更加重要些会烙,單個方法耗時精度不高也是可以接受的,所以這個設(shè)置也是沒問題的筒捺。

總結(jié)來說柏腻,定時抓取主線程調(diào)用棧的方式雖然精準(zhǔn)度不夠高,但也是夠用的系吭。

第二種方法是五嫂,對 objc_msgSend 方法進(jìn)行 hook 來掌握所有方法的執(zhí)行耗時。

hook 方法的意思是肯尺,在原方法開始執(zhí)行時換成執(zhí)行其他你指定的方法沃缘,或者在原有方法執(zhí)行前后執(zhí)行你指定的方法,來達(dá)到掌握和改變指定方法的目的则吟。
hook objc_msgSend 這種方式的優(yōu)點是非常精確槐臀,而缺點是只能針對 Objective-C 的方法。當(dāng)然氓仲,對于 c 方法和 block 也不是沒有辦法水慨,你可以使用 libffi 的 ffi_call 來達(dá)成hook,但缺點就是編寫維護(hù)相關(guān)工具門檻高敬扛。
綜上晰洒,如果對于檢查結(jié)果精準(zhǔn)度要求高的話,我比較推薦你使用 hook objc_msgSend 方式來檢查啟動方法的執(zhí)行耗時啥箭。


如何做一個方法級別啟動耗時檢查工具來輔助分析和監(jiān)控谍珊?

具體的原理涉及到了匯編,這里就不作多說了急侥,有需要了解的可以去找戴銘老師的專欄課

耗時檢查的完整代碼砌滞,可以在開源項目里查看。在需要檢測耗時時間的地方調(diào)用[SMCallTrace start]坏怪,結(jié)束時調(diào)用stopsave 就可以打印出方法的調(diào)用層級和耗時了贝润。
你還可以設(shè)置最大深度和最小耗時檢測,來過濾不需要看到的信息陕悬。
有了這樣一個檢查方法耗時的工具题暖,你就可以在每個版本開發(fā)結(jié)束后執(zhí)行一次檢查按傅,統(tǒng)計總耗時以及啟動階段每個方法的耗時捉超,有針對性地觀察啟動速度慢的問題胧卤。如果你在線上
做個灰度開關(guān),還可以監(jiān)控線上啟動慢的一些特殊情況


啟動速度優(yōu)化和監(jiān)控的重要性不言而喻拼岳,加快 App 的啟動速度對用戶的體驗提升是最大的枝誊。
啟動速度的優(yōu)化也有粗有細(xì):粗上來講,這需要對啟動階段功能進(jìn)行分類整理惜纸,合理地將和首屏無關(guān)的功能滯后叶撒,放到首屏渲染完成之后,保證大頭兒沒有問題耐版;細(xì)的來講祠够,這就
需要些匠人精神,使用合適的工具粪牲,針對每個方法進(jìn)行逐個分析古瓤、優(yōu)化,每個階段都做到極致腺阳。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末落君,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子亭引,更是在濱河造成了極大的恐慌绎速,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焙蚓,死亡現(xiàn)場離奇詭異纹冤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)主届,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門赵哲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人君丁,你說我怎么就攤上這事枫夺。” “怎么了绘闷?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵橡庞,是天一觀的道長。 經(jīng)常有香客問我印蔗,道長扒最,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任华嘹,我火速辦了婚禮吧趣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己强挫,他們只是感情好岔霸,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著俯渤,像睡著了一般呆细。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上八匠,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天絮爷,我揣著相機(jī)與錄音,去河邊找鬼梨树。 笑死坑夯,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的抡四。 我是一名探鬼主播渊涝,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼床嫌!你這毒婦竟也來了跨释?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤厌处,失蹤者是張志新(化名)和其女友劉穎鳖谈,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阔涉,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡缆娃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瑰排。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贯要。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖椭住,靈堂內(nèi)的尸體忽然破棺而出崇渗,到底是詐尸還是另有隱情,我是刑警寧澤京郑,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布宅广,位于F島的核電站,受9級特大地震影響些举,放射性物質(zhì)發(fā)生泄漏跟狱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一户魏、第九天 我趴在偏房一處隱蔽的房頂上張望驶臊。 院中可真熱鬧挪挤,春花似錦、人聲如沸关翎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笤休。三九已至,卻和暖如春症副,著一層夾襖步出監(jiān)牢的瞬間店雅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工贞铣, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留闹啦,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓辕坝,卻偏偏與公主長得像窍奋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子酱畅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348