冷啟動(dòng)(Cold launch)耗時(shí)才是我們需要測(cè)量的重要數(shù)據(jù),為了準(zhǔn)確測(cè)量冷啟動(dòng)耗時(shí),測(cè)量前需要重啟設(shè)備。在 main() 方法執(zhí)行前測(cè)量是很難的,好在 dyld 提供了內(nèi)建的測(cè)量方法:在 Xcode 中 Edit scheme -> Run -> Auguments 將環(huán)境變量 DYLD_PRINT_STATISTICS 設(shè)為 1。控制臺(tái)輸出的內(nèi)容如下:
Total pre-main time: 228.41 milliseconds (100.0%)
dylib loading time: 82.35 milliseconds (36.0%)
rebase/binding time: 6.12 milliseconds (2.6%)
ObjC setup time: 7.82 milliseconds (3.4%)
initializer time: 132.02 milliseconds (57.8%)
slowest intializers :
libSystem.B.dylib : 122.07 milliseconds (53.4%)
CoreFoundation : 5.59 milliseconds (2.4%)
APP啟動(dòng)時(shí)間計(jì)算公式
App總啟動(dòng)時(shí)間 = t1(main()之前的加載時(shí)間) + t2(main()之后的加載時(shí)間)。
t1 = 系統(tǒng)dylib(動(dòng)態(tài)鏈接庫(kù))和自身App可執(zhí)行文件的加載
t2 = main方法執(zhí)行之后到AppDelegate類中的- (BOOL)Application:(UIApplication *)Application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法執(zhí)行結(jié)束前這段時(shí)間,主要是構(gòu)建第一個(gè)界面,并完成渲染展示
啟動(dòng)流程
main()調(diào)用之前加載過(guò)程
exec() 是一個(gè)系統(tǒng)調(diào)用。系統(tǒng)內(nèi)核把應(yīng)用映射到新的地址空間,且每次起始位置都是隨機(jī)的(因?yàn)槭褂?ASLR)。并將起始位置到 0x000000 這段范圍的進(jìn)程權(quán)限都標(biāo)記為不可讀寫不可執(zhí)行。如果是 32 位進(jìn)程,這個(gè)范圍至少是 4KB;對(duì)于 64 位進(jìn)程則至少是 4GB。NULL 指針引用和指針截?cái)嗾`差都是會(huì)被它捕獲。
dylib loading
從主執(zhí)行文件的 header 獲取到需要加載的所依賴動(dòng)態(tài)庫(kù)列表,而 header 早就被內(nèi)核映射過(guò)。然后它需要找到每個(gè) dylib,然后打開文件讀取文件起始位置,確保它是 Mach-O 文件。接著會(huì)找到代碼簽名并將其注冊(cè)到內(nèi)核。然后在 dylib 文件的每個(gè) segment 上調(diào)用 mmap()。應(yīng)用所依賴的 dylib 文件可能會(huì)再依賴其他 dylib,所以 dyld 所需要加載的是動(dòng)態(tài)庫(kù)列表一個(gè)遞歸依賴的集合。一般應(yīng)用會(huì)加載 100 到 400 個(gè) dylib 文件,但大部分都是系統(tǒng) dylib,它們會(huì)被預(yù)先計(jì)算和緩存起來(lái),加載速度很快
main()函數(shù)調(diào)用之前我們可以優(yōu)化的點(diǎn)有:
不使用xib,直接視用代碼加載首頁(yè)視圖。
NSUserDefaults實(shí)際上是在Library文件夾下會(huì)生產(chǎn)一個(gè)plist文件,如果文件太大的話一次能讀取到內(nèi)存中可能很耗時(shí),這個(gè)影響需要評(píng)估,如果耗時(shí)很大的話需要拆分(需考慮老版本覆蓋安裝兼容問(wèn)題)。
每次用NSLog方式打印會(huì)隱式的創(chuàng)建一個(gè)Calendar, 僅僅針對(duì)內(nèi)測(cè)版輸出log。
梳理應(yīng)用啟動(dòng)時(shí)發(fā)送的所有網(wǎng)絡(luò)請(qǐng)求,統(tǒng)一在異步線程請(qǐng)求。
并行初始化各個(gè)業(yè)務(wù)。
優(yōu)化方案
減少framework引用
刪除無(wú)用類,無(wú)用函數(shù)
減少+load 函數(shù)使用,使用 +initialize 來(lái)替代 +load
main()調(diào)用之后, 優(yōu)化內(nèi)容
思路
launcherImage圖片盡量小,實(shí)測(cè)這個(gè)大小會(huì)影響啟動(dòng)速度
Splash 不要Xib,直接用代碼盡量簡(jiǎn)單
將需要執(zhí)行的處理,放入不同的block內(nèi),并發(fā)到不同的queue中進(jìn)行。
提供串行隊(duì)列,執(zhí)行有依賴的邏輯
提供group,對(duì)彼此依賴不明確,但需要整體執(zhí)行完成后,進(jìn)行處理的業(yè)務(wù),提供dispatch_group功能滿足需求。
對(duì)于MainThread有需要的業(yè)務(wù),提供mainThread 支持