基本概念
App的啟動優(yōu)化指的是減少App的啟動時間校镐,啟動方式分為兩種:『冷啟動』和『熱啟動』。
-
冷啟動
:App啟動時,如果在內(nèi)存中沒有App的相關數(shù)據(jù)项栏,必須要從磁盤中重新載入到內(nèi)存中亡资,這種啟動方式叫做冷啟動。在iOS中板驳,殺掉App后重新打開即為冷啟動方式。 -
熱啟動
:App啟動時碍拆,如果內(nèi)存中已包含App的相關數(shù)據(jù)若治,不需要從磁盤中載入慨蓝,這種啟動方式叫做熱啟動。在iOS中端幼,通過home鍵退出App后再打開礼烈,即為熱啟動方式。
一般來說啟動優(yōu)化指的是針對冷啟動方式的優(yōu)化婆跑,啟動時間也分為兩個階段:『main()函數(shù)之前』和『main()函數(shù)之后』济丘。
Main()函數(shù)之前
main()函數(shù)之前
即pre-main
階段,這個階段的啟動時間是沒法自主統(tǒng)計的洽蛀,只能由系統(tǒng)反饋摹迷,在Xcode中通過簡單配置即可查看。
在Xcode中新建一個項目郊供,然后在菜單中峡碉,選擇Product
--> Scheme
--> Edit Scheme
,找到Run
--> Environment Variables
驮审,在這里添加一個name為DYLD_PRINT_STATISTICS
的環(huán)境變量鲫寄,并將其value設置為1
。再運行項目疯淫,即可在console中查看到下面所示的pre-main time
地来。
Total pre-main time: 121.09 milliseconds (100.0%)
dylib loading time: 31.90 milliseconds (26.3%)
rebase/binding time: 37.44 milliseconds (30.9%)
ObjC setup time: 6.25 milliseconds (5.1%)
initializer time: 45.49 milliseconds (37.5%)
slowest intializers :
libSystem.B.dylib : 7.99 milliseconds (6.6%)
libBacktraceRecording.dylib : 10.73 milliseconds (8.8%)
libMainThreadChecker.dylib : 23.81 milliseconds (19.6%)
從輸出結果可知,pre-main總共耗時123.40ms熙掺,分為如下4個階段:
-
dylib loading
:加載動態(tài)庫未斑。動態(tài)庫越多,越耗時币绩。
-
rebase/binding
:偏移修正和符號綁定蜡秽。rebase(偏移修正)
:App在編譯時,會生成二進制文件缆镣,在文件內(nèi)部的所有方法和函數(shù)芽突,都記錄了一個偏移地址。在運行時董瞻,系統(tǒng)會為二進制文件分配一個ASLR隨機值(Address Space Layout Randomization寞蚌,地址空間布局隨機化)
,并將隨機值插入到二進制文件的開頭钠糊,每個方法和函數(shù)加載在內(nèi)存中的真實地址即為:ASLR隨機值 + 偏移值
挟秤。這樣,每次運行眠蚂,都會重新分配ASLR隨機值
煞聪,都要偏移修正重新加載,這就導致耗時逝慧。binding(符號綁定)
:在MacOS和iOS中昔脯,方法和函數(shù)并不是直接訪問的啄糙,而是通過其在MachO
文件中對應的符號來訪問。比如說云稚,NSLog
是存在于Foundation動態(tài)庫的方法隧饼,在編譯期,會在Mach0文件里創(chuàng)建一個與之對應的符號!NSLog
静陈,此時符號指向一個無意義的隨機地址燕雁,MacO文件也是存在于磁盤中。然后在運行時鲸拥,MacO文件會被拷貝加載到內(nèi)存中拐格,此時會將NSLog
方法在內(nèi)存中的真實地址和符號!NSLog
關聯(lián)起來,這就是符號綁定刑赶,在這個過程中也存在耗時捏浊。
-
ObjC setup
:OC類的注冊。OC類越多撞叨,耗時越久金踪。
-
initializer
:執(zhí)行load
方法和構造函數(shù)。從輸出結果可知這個
initializer
過程耗時最多的是libSystem.B.dylib
牵敷、libBacktraceRecording.dylib
以及libMainThreadChecker.dylib
這三個動態(tài)庫胡岔。
針對這個階段,優(yōu)化建議如下:
- 除了系統(tǒng)自帶的動態(tài)庫枷餐,開發(fā)過程中盡量不要自己添加外部動態(tài)庫靶瘸,蘋果官方建議項目中使用的外部動態(tài)庫最好不要超過6個,如果超過6個尖淘,需要合并動態(tài)庫奕锌。
- 減少自定義的OC類,對于老項目村生,及時刪掉廢棄的類和方法。
- 盡量少使用
+load
方法饼丘,將相關操作放在+initialize
方法中實現(xiàn)趁桃。 - 對于
swift
來說,多使用struct
肄鸽。 - 二進制重排卫病,減少內(nèi)存訪問的耗時。
Main()函數(shù)之后
從main()
函數(shù)開始至applicationWillFinishLaunching
結束典徘,統(tǒng)一稱為main()
函數(shù)之后的部分蟀苛。耗時因素主要是以下幾種:
- 執(zhí)行
main()
函數(shù)的耗時 - 執(zhí)行
applicationWillFinishLaunching
的耗時 -
rootViewController
及其childViewController
的加載、view
及其subviews
的加載
這個階段的啟動時間可以自主統(tǒng)計逮诲,根據(jù)各App的業(yè)務代碼來決定帜平。優(yōu)化建議如下:
- 優(yōu)化代碼邏輯幽告,能懶加載的懶加載,能延遲的延遲裆甩,能放后臺初始化的放后臺冗锁,能使用多線程來初始化的,就使用多線程,嗤栓,盡量不要占用主線程的啟動時間冻河。
- 盡量使用純代碼來開發(fā),少用
Xib
或者Storyboard
茉帅。 - 對于比較復雜的首頁叨叙,先加載本地緩存進行顯示,再在數(shù)據(jù)請求成功后更新最新信息堪澎。
推薦閱讀
1. iOS原理 App的啟動優(yōu)化2:二進制重排
2. iOS App 啟動性能優(yōu)化
3. iOS中的動態(tài)庫和靜態(tài)庫