iOS - 優化App冷啟動速度

1. App的啟動分為三個主要階段:

  • main()函數執行前

  • main()函數執行后(從main函數執行,到設置self.window.rootViewController)

  • 首屏渲染完成后(從設置self.window.rootViewController到didFinishLaunchWithOptions方法作用域結束)

main函數執行前,系統會做的事情:
  • 加載可執行文件(App的.o文件集合)

  • 加載動態鏈接庫,進行rebase指針調整和bind符號綁定

  • Objc運行時的初始處理,包括Objc相關類注冊、category注冊、selector唯一性檢查等

  • 初始化,包括了執行+load()方法、attribute((constructor))修飾的函數的調用、創建C++靜態全局變量。

main()函數執行后:

main()函數執行后的階段,指的是從main()函數執行開始,到appDelegate的didFinishLaunchingWithOpentions方法里首屏渲染相關方法執行完成。

這里應該是從功能上梳理出哪些是首屏渲染必要的初始化功能,哪些是App啟動必要的初始化功能,哪些是只需要在對應功能開始使用時才需要初始化的,將這些放到各自合適的階段執行。

首屏渲染完成后:

首屏渲染后的這個階段,指的是didFinishLaunchWithOptions方法作用域內執行首屏渲染之后的所有方法執行完成,即從 設置了self.window.rootViewController開始 到 didFinishLaunchWithOptions方法作用域 結束。

首屏渲染完成后用戶就可以看到App的首頁信息了,把這個階段內卡住主線程的方法解決掉就可以了。

注解:
  • App啟動后,首先加載可執行文件,然后加載dyld,然后加載所有依賴庫,然后調用所有的+load(),然后調用main(),然后調用UIApplicationMain(),然后調用AppDelegate的代理didFinishLaunchWithOptions.

  • 可執行文件是指Mach-O格式的文件,也就是App中所有.o文件的集合體,從這里可以獲取dyld的路徑,然后加載dyld。

  • dyld是指蘋果的動態鏈接器,加載dyld后,就會去初始化運行環境,開啟緩存策略,加載依賴庫,并且會調用每一個依賴庫的初始化方法,包括RunTime也是在這里被初始化的,當所有的依賴庫都被初始化完成后,RunTime會對項目中所有的類進行類初始化,調用所有的+load()方法,最后dyld會返回main函數地址,然后main函數會被調用。

  • 知曉上述的流程后,我們就明白為什么優化啟動速度,要去減少動態庫加載,要少用+load(),理論明白了之后,我們就要看看具體怎么做了。

  • 動態庫是指可以共享的代碼文件、資源文件、頭文件等的打包集合體。在Xcode->Targets->General->Link Binary With Libraries可以檢查自己的庫,

  • 減少+load()的使用,將里面的內容放到渲染結束后去做,或者用+initialize()代替。+load()方法在main()調用前就會調用,而+initialize()方法是在類第一次收到消息后,才會調用,兩者的區別可以參考這里

    Main函數調用前

    Main函數調用后

2.具體優化方法

(1)減少+load()的使用

使用+initialize()的方法代替+load(),注意把邏輯移動到+initialize()時,要注意避免+initialize()的重復調用問題,可以使用dispatch_once()讓邏輯只執行一次。

(2)對多個動態庫進行合并

蘋果公司建議使用更少的動態庫,并且建議在使用動態庫的數量較多時,盡量將多個動態庫進行合并。數量上,蘋果公司最多可以支持6個非系統動態庫合并為一個。

(3)優化類、方法、全局變量

減少加載啟動后不會去使用的類或方法;控制C++全局變量的數量

(4)功能級別的啟動優化

main()開始執行后到首屏渲染完成前,只處理首屏相關的業務,其他的都放到首屏渲染完成后去做。

(5)方法級別的啟動優化

首先檢查首屏渲染完成前主線程上的耗時操作,將沒必要的操作滯后或異步。通常耗時操作有:加載、編輯、存儲圖片和文件等資源。

3. 查看耗時

(1)查看Main()調用前花費的總時間

在Product->Scheme->Edit Scheme->Run->Arguments->Environment Variables->DYLD_PRINT_STATISTICS設置為YES,就可以在控制臺中查看main函數執行前總共花費的多長時間。

設置環境變量.png

控制臺會輸出pre-main的總時間.png

(2)查看加載了多少動態庫

在Product->Scheme->Edit Scheme->Run->Diagnostics->Logging->勾選Dynamic Library Loads,就可以在控制臺中查看本項目中加載的所有動態庫(包括系統的和自己的)。


image.png
(3)查看Main函數啟動后的耗時

main函數調用后的耗時,可以使用一些工具來監控,有一種非常笨但是很實用的方法,就是通過打點,在didFinishLaunchingWithOptions開始前打一個點,在App顯示完成第一個界面再打一個點,計算兩個點之間的耗時,就可以知道main函數調用后到界面顯示出來的耗時了,但是這樣只能籠統的知道總的耗時,并不能準確的知道時間花在了哪里。

如果想用這個打點法的話,推薦一個打點工具BLStopwatch

如果想準確知道時間都花在了哪里,推薦使用下面兩種方法。

4. 監控App啟動耗時,精準找出時間都花在了哪里,方便逐一優化

準確監控方法有兩種:
  1. 定時抓取主線程上的方法調用堆棧,計算一段時間里各個方法的耗時。Xcode自帶的Time Profiler就是用的這種方法。

  2. 對objc_msgSend方法進行hook來掌握所有方法的執行耗時。

根據這兩種方法,分別實現兩個工具,來監控耗時

由于能力有限,我只根據第一種方法做出來一個計算某個線程的耗時工具,放在了這里BSMonitorTimeTool,大致思路如下:

(1). 通過定時器,每隔0.01s,獲取一次主線程的函數堆棧,將函數名稱、函數地址、函數耗時模型化為TimeModel,保存在callStackDict中,其中key為函數地址,value為TimeModel

(2). 定時執行的回調中,每次都判斷函數地址是否存在,如果已經存在此函數地址,就講對應的TimeModel中的耗時增加0.01s;如果不存在此函數地址,就初始化一個TimeModel,并將時間設置為0.01s。

(3). 當主界面顯示完成之后,輸出此callStackDict,即可查看主線程中每個方法的耗時

5. 歡迎大家指正錯誤,希望能夠共同進步

本文章是參考了很多大佬的文章,歡迎各位前去膜拜

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容