iOS - 優(yōu)化App冷啟動(dòng)速度

1. App的啟動(dòng)分為三個(gè)主要階段:

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

  • main()函數(shù)執(zhí)行后(從main函數(shù)執(zhí)行咳榜,到設(shè)置self.window.rootViewController)

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

main函數(shù)執(zhí)行前爽锥,系統(tǒng)會(huì)做的事情:
  • 加載可執(zhí)行文件(App的.o文件集合)

  • 加載動(dòng)態(tài)鏈接庫,進(jìn)行rebase指針調(diào)整和bind符號綁定

  • Objc運(yùn)行時(shí)的初始處理臣樱,包括Objc相關(guān)類注冊腮考、category注冊、selector唯一性檢查等

  • 初始化踩蔚,包括了執(zhí)行+load()方法、attribute((constructor))修飾的函數(shù)的調(diào)用飘蚯、創(chuàng)建C++靜態(tài)全局變量福也。

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

main()函數(shù)執(zhí)行后的階段,指的是從main()函數(shù)執(zhí)行開始暴凑,到appDelegate的didFinishLaunchingWithOpentions方法里首屏渲染相關(guān)方法執(zhí)行完成。

這里應(yīng)該是從功能上梳理出哪些是首屏渲染必要的初始化功能穴店,哪些是App啟動(dòng)必要的初始化功能拿穴,哪些是只需要在對應(yīng)功能開始使用時(shí)才需要初始化的,將這些放到各自合適的階段執(zhí)行默色。

首屏渲染完成后:

首屏渲染后的這個(gè)階段,指的是didFinishLaunchWithOptions方法作用域內(nèi)執(zhí)行首屏渲染之后的所有方法執(zhí)行完成呕诉,即從 設(shè)置了self.window.rootViewController開始 到 didFinishLaunchWithOptions方法作用域 結(jié)束。

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

注解:
  • App啟動(dòng)后伊者,首先加載可執(zhí)行文件,然后加載dyld挖诸,然后加載所有依賴庫,然后調(diào)用所有的+load(),然后調(diào)用main()斩芭,然后調(diào)用UIApplicationMain(),然后調(diào)用AppDelegate的代理didFinishLaunchWithOptions.

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

  • dyld是指蘋果的動(dòng)態(tài)鏈接器此再,加載dyld后,就會(huì)去初始化運(yùn)行環(huán)境摘符,開啟緩存策略策吠,加載依賴庫,并且會(huì)調(diào)用每一個(gè)依賴庫的初始化方法猴抹,包括RunTime也是在這里被初始化的,當(dāng)所有的依賴庫都被初始化完成后蝙砌,RunTime會(huì)對項(xiàng)目中所有的類進(jìn)行類初始化,調(diào)用所有的+load()方法择克,最后dyld會(huì)返回main函數(shù)地址,然后main函數(shù)會(huì)被調(diào)用壹堰。

  • 知曉上述的流程后道偷,我們就明白為什么優(yōu)化啟動(dòng)速度缀旁,要去減少動(dòng)態(tài)庫加載勺鸦,要少用+load()目木,理論明白了之后,我們就要看看具體怎么做了军拟。

  • 動(dòng)態(tài)庫是指可以共享的代碼文件誓禁、資源文件、頭文件等的打包集合體摹恰。在Xcode->Targets->General->Link Binary With Libraries可以檢查自己的庫,

  • 減少+load()的使用姑宽,將里面的內(nèi)容放到渲染結(jié)束后去做闺阱,或者用+initialize()代替炮车。+load()方法在main()調(diào)用前就會(huì)調(diào)用酣溃,而+initialize()方法是在類第一次收到消息后,才會(huì)調(diào)用扛或,兩者的區(qū)別可以參考這里

    Main函數(shù)調(diào)用前

    Main函數(shù)調(diào)用后

2.具體優(yōu)化方法

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

使用+initialize()的方法代替+load()亿絮,注意把邏輯移動(dòng)到+initialize()時(shí)麸拄,要注意避免+initialize()的重復(fù)調(diào)用問題黔姜,可以使用dispatch_once()讓邏輯只執(zhí)行一次。

(2)對多個(gè)動(dòng)態(tài)庫進(jìn)行合并

蘋果公司建議使用更少的動(dòng)態(tài)庫淮椰,并且建議在使用動(dòng)態(tài)庫的數(shù)量較多時(shí)纳寂,盡量將多個(gè)動(dòng)態(tài)庫進(jìn)行合并。數(shù)量上毙芜,蘋果公司最多可以支持6個(gè)非系統(tǒng)動(dòng)態(tài)庫合并為一個(gè)。

(3)優(yōu)化類晦雨、方法隘冲、全局變量

減少加載啟動(dòng)后不會(huì)去使用的類或方法;控制C++全局變量的數(shù)量

(4)功能級別的啟動(dòng)優(yōu)化

main()開始執(zhí)行后到首屏渲染完成前展辞,只處理首屏相關(guān)的業(yè)務(wù),其他的都放到首屏渲染完成后去做罗珍。

(5)方法級別的啟動(dòng)優(yōu)化

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

3. 查看耗時(shí)

(1)查看Main()調(diào)用前花費(fèi)的總時(shí)間

在Product->Scheme->Edit Scheme->Run->Arguments->Environment Variables->DYLD_PRINT_STATISTICS設(shè)置為YES,就可以在控制臺(tái)中查看main函數(shù)執(zhí)行前總共花費(fèi)的多長時(shí)間霹购。

設(shè)置環(huán)境變量.png

控制臺(tái)會(huì)輸出pre-main的總時(shí)間.png

(2)查看加載了多少動(dòng)態(tài)庫

在Product->Scheme->Edit Scheme->Run->Diagnostics->Logging->勾選Dynamic Library Loads朋腋,就可以在控制臺(tái)中查看本項(xiàng)目中加載的所有動(dòng)態(tài)庫(包括系統(tǒng)的和自己的)膜楷。


image.png
(3)查看Main函數(shù)啟動(dòng)后的耗時(shí)

main函數(shù)調(diào)用后的耗時(shí)赌厅,可以使用一些工具來監(jiān)控轿塔,有一種非常笨但是很實(shí)用的方法,就是通過打點(diǎn)勾缭,在didFinishLaunchingWithOptions開始前打一個(gè)點(diǎn),在App顯示完成第一個(gè)界面再打一個(gè)點(diǎn)俩由,計(jì)算兩個(gè)點(diǎn)之間的耗時(shí),就可以知道m(xù)ain函數(shù)調(diào)用后到界面顯示出來的耗時(shí)了审胚,但是這樣只能籠統(tǒng)的知道總的耗時(shí)礼旅,并不能準(zhǔn)確的知道時(shí)間花在了哪里洽洁。

如果想用這個(gè)打點(diǎn)法的話,推薦一個(gè)打點(diǎn)工具BLStopwatch

如果想準(zhǔn)確知道時(shí)間都花在了哪里饿自,推薦使用下面兩種方法。

4. 監(jiān)控App啟動(dòng)耗時(shí)复唤,精準(zhǔn)找出時(shí)間都花在了哪里烛卧,方便逐一優(yōu)化

準(zhǔn)確監(jiān)控方法有兩種:
  1. 定時(shí)抓取主線程上的方法調(diào)用堆棧,計(jì)算一段時(shí)間里各個(gè)方法的耗時(shí)总放。Xcode自帶的Time Profiler就是用的這種方法。

  2. 對objc_msgSend方法進(jìn)行hook來掌握所有方法的執(zhí)行耗時(shí)局雄。

根據(jù)這兩種方法,分別實(shí)現(xiàn)兩個(gè)工具蜈漓,來監(jiān)控耗時(shí)

由于能力有限,我只根據(jù)第一種方法做出來一個(gè)計(jì)算某個(gè)線程的耗時(shí)工具融虽,放在了這里BSMonitorTimeTool,大致思路如下:

(1). 通過定時(shí)器驼侠,每隔0.01s谆吴,獲取一次主線程的函數(shù)堆棧,將函數(shù)名稱句狼、函數(shù)地址、函數(shù)耗時(shí)模型化為TimeModel腻菇,保存在callStackDict中,其中key為函數(shù)地址糖耸,value為TimeModel

(2). 定時(shí)執(zhí)行的回調(diào)中丘薛,每次都判斷函數(shù)地址是否存在,如果已經(jīng)存在此函數(shù)地址洋侨,就講對應(yīng)的TimeModel中的耗時(shí)增加0.01s;如果不存在此函數(shù)地址边苹,就初始化一個(gè)TimeModel裁僧,并將時(shí)間設(shè)置為0.01s。

(3). 當(dāng)主界面顯示完成之后锅知,輸出此callStackDict,即可查看主線程中每個(gè)方法的耗時(shí)

5. 歡迎大家指正錯(cuò)誤售睹,希望能夠共同進(jìn)步

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捶枢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子谨胞,更是在濱河造成了極大的恐慌蒜鸡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叶沛,死亡現(xiàn)場離奇詭異,居然都是意外死亡灰署,警方通過查閱死者的電腦和手機(jī)局嘁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肴茄,“玉大人,你說我怎么就攤上這事独郎∶渡模” “怎么了谓谦?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卢肃。 經(jīng)常有香客問我,道長莫湘,這世上最難降的妖魔是什么郑气? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮尾组,結(jié)果婚禮上示弓,老公的妹妹穿的比我還像新娘呵萨。我一直安慰自己,他們只是感情好囱皿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布忱嘹。 她就那樣靜靜地躺著,像睡著了一般德谅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上愧驱,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天椭盏,我揣著相機(jī)與錄音,去河邊找鬼掏颊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛乌叶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播事扭,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼乐横,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了罐农?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤涵亏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后溯乒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡矛纹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年或南,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片采够。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡冰垄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出虹茶,到底是詐尸還是另有隱情,我是刑警寧澤董济,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布要门,位于F島的核電站,受9級特大地震影響欢搜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炒瘟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一唧领、第九天 我趴在偏房一處隱蔽的房頂上張望雌续。 院中可真熱鬧斩个,春花似錦驯杜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藤肢。三九已至,卻和暖如春嘁圈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钞澳。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工涨缚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脓魏。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像轧拄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子拄丰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

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