想做app啟動(dòng)優(yōu)化侠鳄,當(dāng)然是先了解app啟動(dòng)听诸,什么時(shí)候開始桅狠?什么時(shí)候結(jié)束?哪里是我們可以去優(yōu)化的地方趴荸?
app啟動(dòng)開始:加載應(yīng)用的可執(zhí)行文件Mach-O
app啟動(dòng)結(jié)束:調(diào)用didFinishLaunchingWithOptions
- app啟動(dòng)這里我分成兩個(gè)階段:pre-main階段和main()階段
pre-main階段
1.加載應(yīng)用的可執(zhí)行文件Mach-O
2.加載動(dòng)態(tài)鏈接庫加載器dyld(dynamic loader)
3.dyld遞歸加載應(yīng)用所有依賴的dylib (dynamic library動(dòng)態(tài)鏈接庫)
main()階段
1.dyld調(diào)用main()
2.調(diào)用UIApplicationMain()
3.調(diào)用applicationWillFinishLaunching
4.調(diào)用didFinishLaunchingWithOptions
好了儒溉,任務(wù)分好了,開始干活7⒍邸6倩痢!
注:優(yōu)化時(shí)間=優(yōu)化前時(shí)間-優(yōu)化后時(shí)間
//下面使用模擬器酝豪,建議使用真機(jī)+release+冷啟動(dòng)涛碑,得到數(shù)據(jù)才最真實(shí)
一.pre-main階段
在 Xcode 中 Edit scheme -> Run -> Arguments 將環(huán)境變量DYLD_PRINT_STATISTICS 設(shè)為1 ,打鉤
根據(jù)官方給的四個(gè)time,我們可以分為四個(gè)步驟
- 1.load dylibs
這一階段dyld會(huì)分析應(yīng)用依賴的dylib孵淘,找到其mach-o文件蒲障,打開和讀取這些文件并驗(yàn)證其有效性瑞眼,遞歸加載所有依賴庫单山。
一般情況下栅螟,iOS應(yīng)用會(huì)加載幾百個(gè)dylibs蚂维,其中大部分是系統(tǒng)庫,這部分對(duì)dylib的加載做了緩存操作毙籽。
所以洞斯,依賴的dylib越少越好,我們可以優(yōu)化的地方:
1.盡可能減少依賴的dylib的數(shù)量
2.使用開銷較小的dylib
- 2.rebase/bind
在dylib的加載過程中坑赡,系統(tǒng)為了安全考慮烙如,引入了ASLR(Address Space Layout Randomization)技術(shù)和代碼簽名。由于ASLR的存在垮衷,鏡像(Image厅翔,包括可執(zhí)行文件乖坠、dylib和bundle)會(huì)在隨機(jī)的地址上加載搀突,和之前指針指向的地址(preferred_address)會(huì)有一個(gè)偏差(slide),dyld需要修正這個(gè)偏差熊泵,來指向正確的地址仰迁。
Rebase在前,Bind在后顽分,Rebase做的是將鏡像讀入內(nèi)存徐许,修正鏡像內(nèi)部的指針,性能消耗主要在IO卒蘸。Bind做的是查詢符號(hào)表雌隅,設(shè)置指向鏡像外部的指針,性能消耗主要在CPU計(jì)算缸沃。
所以恰起,指針數(shù)量越少越好,我們可以做的優(yōu)化有:
1.減少類Class趾牧、方法selector检盼、分類category的數(shù)量
2.減少C++虛函數(shù)的的數(shù)量(創(chuàng)建虛函數(shù)表有開銷)
3.使用Swift structs(內(nèi)部做了優(yōu)化,符號(hào)數(shù)量更少)
- 3.Objc setup
大部分ObjC初始化工作已經(jīng)在Rebase/Bind階段做完了翘单,這一步dyld會(huì)注冊(cè)所有聲明過的ObjC類吨枉,將分類插入到類的方法列表里,再檢查每個(gè)selector的唯一性哄芜。
在這一步倒沒什么優(yōu)化可做的貌亭,Rebase/Bind階段優(yōu)化好了,這一步的耗時(shí)也會(huì)減少认臊。
- 4.Initializers
到了這一階段属提,dyld開始運(yùn)行程序的初始化函數(shù),調(diào)用每個(gè)Objc類和分類的+load方法,調(diào)用C/C++ 中的構(gòu)造器函數(shù)(用attribute((constructor))修飾的函數(shù))冤议,和創(chuàng)建非基本類型的C++靜態(tài)全局變量斟薇。Initializers階段執(zhí)行完后,dyld開始調(diào)用main()函數(shù)恕酸。
在這一步堪滨,我們可以做的優(yōu)化有:
1.少在類的+load方法里做事情,盡量把這些事情推遲到+initiailize
2.減少構(gòu)造器函數(shù)個(gè)數(shù)蕊温,在構(gòu)造器函數(shù)里少做些事情
3.減少C++靜態(tài)全局變量的個(gè)數(shù)
二.main()階段
在mian.m和AppDelegate.m中加上以上代碼袱箱,可以獲得main()之后啟動(dòng)時(shí)間
這個(gè)階段牽扯到我們的業(yè)務(wù),功能的實(shí)現(xiàn)
- 參考個(gè)人項(xiàng)目:在didFinishLaunchingWithOptions方法中
是否顯示引導(dǎo)頁义矛、是否需要登錄发笔、新版本的檢測(cè)、注冊(cè)推送凉翻、網(wǎng)絡(luò)狀態(tài)的檢測(cè)了讨、三方庫的配置和初始化等
首頁控制器的加載,Tabbar和Nav控制器的加載等
根據(jù)個(gè)人經(jīng)驗(yàn)做出一些優(yōu)化建議:
這一階段的優(yōu)化主要是減少didFinishLaunchingWithOptions方法里的時(shí)間制轰,所以我們要確認(rèn)每個(gè)任務(wù)的時(shí)間量(利用CFAbsoluteTimeGetCurrent()方法去計(jì)算前计,計(jì)算方法參考上面),然后找出影響大的去做優(yōu)化(當(dāng)然時(shí)間容許全部?jī)?yōu)化最好)
1.滿足業(yè)務(wù)前提下垃杖,didFinishLaunchingWithOptions在主線程里做的事情越少越好
將三方庫和一些功能延遲執(zhí)行男杈,比如放在首頁的viewDidAppear方法中
將三方庫和一些功能異步執(zhí)行,比如異步檢測(cè)新版本的更新调俘,注冊(cè)推送
避免多余復(fù)雜的計(jì)算伶棒,比如合適的算法減少運(yùn)算量和內(nèi)存消耗
采用性能好的API,輕量級(jí)的對(duì)象
首頁控制器用純代碼方式來構(gòu)建