APP的啟動可以分為2種:
- 冷啟動
(Cold Launch)
:從零開始啟動APP仇矾。 - 熱啟動
(Warm Launch)
:APP已經(jīng)在內(nèi)存中入热,在后臺存活著拍棕,再次點擊圖標(biāo)啟動APP。
通常所做的啟動優(yōu)化勺良,都是針對冷啟動做優(yōu)化绰播。
通過添加環(huán)境變量可以打印出APP的啟動時間分析,點擊Edit scheme -> Run -> Arguments
尚困,給Environment Variables
添加一個DYLD_PRINT_STATISTICS
蠢箩,并且把Value
設(shè)置為1
,如下圖:
Total pre-main time: 825.00 milliseconds (100.0%) //main函數(shù)調(diào)用之前總耗時
dylib loading time: 407.70 milliseconds (49.4%)//動態(tài)庫加載耗時
rebase/binding time: 45.62 milliseconds (5.5%)
ObjC setup time: 100.80 milliseconds (12.2%)//OC結(jié)構(gòu)體準(zhǔn)備耗時
initializer time: 270.75 milliseconds (32.8%)//初始化耗時
slowest intializers ://比較慢的加載
libSystem.B.dylib : 7.66 milliseconds (0.9%)
libMainThreadChecker.dylib : 29.10 milliseconds (3.5%)
libglInterpose.dylib : 83.99 milliseconds (10.1%)
libMTLInterpose.dylib : 38.42 milliseconds (4.6%)
xxxxxx : 174.05 milliseconds (21.0%)
如果需要更詳細(xì)的信息事甜,那就將DYLD_PRINT_STATISTICS
改為DYLD_PRINT_STATISTICS_DETAILS
并且Value
也是設(shè)置為1谬泌,打印如下:
total time: 1.9 seconds (100.0%)//總耗時
total images loaded: 459 (443 from dyld shared cache)//鏡像加載耗時
total segments mapped: 49, into 5926 pages with 984 pages pre-fetched
total images loading time: 1.3 seconds (70.1%)
total load time in ObjC: 96.78 milliseconds (4.8%)//加載objc
total debugger pause time: 973.79 milliseconds (48.8%)
total dtrace DOF registration time: 0.12 milliseconds (0.0%)
total rebase fixups: 479,455
total rebase fixups time: 41.04 milliseconds (2.0%)
total binding fixups: 615,196
total binding fixups time: 192.49 milliseconds (9.6%)
total weak binding fixups time: 4.25 milliseconds (0.2%)
total redo shared cached bindings time: 194.69 milliseconds (9.7%)
total bindings lazily fixed up: 0 of 0
total time in initializers and ObjC +load: 259.27 milliseconds (13.0%)
libSystem.B.dylib : 8.80 milliseconds (0.4%)
libBacktraceRecording.dylib : 10.29 milliseconds (0.5%)
Foundation : 2.01 milliseconds (0.1%)
libMainThreadChecker.dylib : 28.12 milliseconds (1.4%)
libViewDebuggerSupport.dylib : 4.90 milliseconds (0.2%)
libglInterpose.dylib : 76.86 milliseconds (3.8%)
libMTLInterpose.dylib : 39.89 milliseconds (2.0%)
LookinServer : 2.89 milliseconds (0.1%)
xxxxxx : 160.63 milliseconds (8.0%)
total symbol trie searches: 1428216
total symbol table binary searches: 0
total images defining weak symbols: 45
total images using weak symbols: 113
一般總時間在800ms以內(nèi)都是比較正常的,如果大于800ms就可以優(yōu)化了逻谦。
冷啟動的三大階段
APP的冷啟動可以概括為3大階段:
dyld
runtime
-
main
dyld
dyld(dynamic link editor)
掌实,它是Apple的動態(tài)鏈接器,可以用來裝載Mach-O
文件(可執(zhí)行文件邦马、動態(tài)庫等)贱鼻。
啟動APP時,dyld
動態(tài)鏈接器會裝載APP的可執(zhí)行文件滋将,同時會遞歸加載所有依賴的動態(tài)庫邻悬。
右鍵點擊XX.app
選擇Show in Finder
,找到XX.app
随闽,右鍵點擊XX.app
選擇顯示包內(nèi)容拘悦,可以發(fā)現(xiàn)里面有個Unix
可執(zhí)行文件,平時我們寫的所有代碼都在這里面了橱脸,這就是一個Mach-O
格式的可執(zhí)行文件础米。
但是我們項目開發(fā)中也會依賴一些動態(tài)庫,比如UIKit
添诉、Foundation
屁桑,這些動態(tài)庫都不是包含在可執(zhí)行文件里面的,可執(zhí)行文件里面只有一些依賴信息栏赴,比如這個項目依賴哪些動態(tài)庫蘑斧,一個動態(tài)庫也可能依賴另一個動態(tài)庫。
在dyld
階段,dyld
動態(tài)鏈接器會裝載可執(zhí)行文件竖瘾,以及檢查可執(zhí)行文件依賴哪些動態(tài)庫沟突,并加載那些動態(tài)庫,一個動態(tài)庫也可能依賴另一個動態(tài)庫捕传,dyld
動態(tài)鏈接器就是這樣一個一個檢查惠拭,遞歸查找,直到裝載完所有的動態(tài)庫到內(nèi)存中庸论。
當(dāng)dyld
把可執(zhí)行文件职辅、動態(tài)庫都裝載完畢后,會通知Runtime
進(jìn)行下一步的處理聂示。
- runtime
啟動APP時域携,runtime
所做的事情有:
1.調(diào)用map_images
函數(shù)進(jìn)行可執(zhí)行文件內(nèi)容的解析和處理
2.在load_images
函數(shù)中調(diào)用call_load_methods
,調(diào)用所有Class
和Category
的+load
方法
3.進(jìn)行各種objc結(jié)構(gòu)的初始化(注冊Objc
類 鱼喉、初始化類對象等等)
4.調(diào)用C++
靜態(tài)初始化器和__attribute__((constructor))
修飾的函數(shù)
到此為止秀鞭,可執(zhí)行文件和動態(tài)庫中所有的符號(Class,Protocol扛禽,Selector锋边,IMP,…)
都已經(jīng)按格式成功加載到內(nèi)存中旋圆,被runtime
所管理。
- main
總結(jié)一下:
APP的啟動由dyld
主導(dǎo)麸恍,將可執(zhí)行文件加載到內(nèi)存灵巧,順便加載所有依賴的動態(tài)庫
并由runtime
負(fù)責(zé)加載成objc
定義的結(jié)構(gòu)
所有初始化工作結(jié)束后,dyld
就會調(diào)用main
函數(shù)
接下來就是UIApplicationMain
函數(shù)抹沪,AppDelegate
的application:didFinishLaunchingWithOptions:
方法
冷啟動優(yōu)化
按照不同的階段:
dyld:
減少動態(tài)庫刻肄、合并一些動態(tài)庫(定期清理不必要的動態(tài)庫)
減少Objc
類、分類的數(shù)量融欧、減少Selector
數(shù)量(定期清理不必要的類敏弃、分類)
減少C++
虛函數(shù)數(shù)量 (因為一旦有虛函數(shù)就要多維護(hù)一張?zhí)摫恚刑摫淼脑捓鋯拥臅r候就要多耗費一點時間)
Swift
盡量使用struct
(因為加載類的時候也耗費時間)runtime:
用+initialize
方法和dispatch_once
取代所有的__attribute__((constructor))
噪馏、C++
靜態(tài)構(gòu)造器麦到、ObjC
的+load
方法。main:
在不影響用戶體驗的前提下欠肾,盡可能將一些操作延遲瓶颠,不要全部都放在didFinishLaunchingWithOptions
方法中,也就是按需加載刺桃。
貼一些啟動優(yōu)化的文檔:
iOS開發(fā)高手課--App 啟動速度怎么做優(yōu)化與監(jiān)控粹淋?
看到這篇啟動優(yōu)化,讓你的App有順滑無比的啟動速度
iOS App冷啟動治理:來自美團(tuán)外賣的實踐
我是如何讓微博綠洲的啟動速度提升30%的
廖威雄: 利用attribute((section()))構(gòu)建初始化函數(shù)表與Linux內(nèi)核init的實現(xiàn)