APP啟動(dòng)流程
- 解析info.plist
- 加載相關(guān)信息
- 沙箱建立, 權(quán)限檢查
- Mach-O加載
- 如果是胖二進(jìn)制文件, 尋找合適當(dāng)前CPU類別的部分
- 加載所有依賴的Mach-O文件(遞歸調(diào)用Mach-O加載的方法)
- 定位內(nèi)部, 外部指針引用, 例如字符串, 函數(shù)等
- 執(zhí)行聲明為attribute((constructor))的C函數(shù)
- 加載類擴(kuò)展(Category)中的方法
- C++靜態(tài)對(duì)象加載, 調(diào)用ObjC的 +load 函數(shù)
- 程序執(zhí)行(main函數(shù)之后)
- 調(diào)用main()
- 調(diào)用UIApplicationMain()
- 調(diào)用applicationWillFinishLaunching
那么APP啟動(dòng)優(yōu)化可以粗暴的分為兩個(gè)主要部分, 分別是:
- main函數(shù)執(zhí)行前
- main函數(shù)執(zhí)行后
main函數(shù)執(zhí)行前
xcode提供了一個(gè)環(huán)境變量可以檢測(cè)main函數(shù)之前的執(zhí)行時(shí)間
在xcode的菜單中選擇Project > Scheme > Edit Scheme...,然后找到 Run > Environment Variables > +, 添加name為DYLD_PRINT_STATISTICSvalue為1的環(huán)境變量.
在程序啟動(dòng)的時(shí)候控制臺(tái)會(huì)打印相關(guān)的信息
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%)
下面逐行解讀
- main之前用了228.41毫秒
- 動(dòng)態(tài)庫(kù)加載用了82.35毫秒
- 指針重定位6.12毫秒
- ObjC類初始化7.82毫秒
- 各種初始化132.02毫秒
- 最慢的初始化, libSystem.B.dylib 122.07以及CoreFoundation 5.59
以上很明顯的看到在main函數(shù)執(zhí)行前的各項(xiàng)耗時(shí)細(xì)節(jié), 網(wǎng)上搜索后也沒(méi)有發(fā)現(xiàn)很直接的明顯可以優(yōu)化的方案.下面是我個(gè)人的整理, 大神們有更好的方案還望補(bǔ)充斧正.
- 移除不需要的動(dòng)態(tài)庫(kù)
- 移除不需要用到的類
- 減少class, selector, category的數(shù)量, 但是從編碼原則和設(shè)計(jì)模式之類的理論會(huì)鼓勵(lì)大家把各個(gè)功能解耦, 其實(shí)這會(huì)增加啟動(dòng)時(shí)間, 這一點(diǎn)就是見(jiàn)仁見(jiàn)智了. Swift推薦使用結(jié)構(gòu)體, 加載會(huì)更快.
- 使用initialize代替load
- 不要使用 atribute((constructor)) 將方法顯式標(biāo)記為初始化器, 而是讓初始化方法調(diào)用時(shí)才執(zhí)行. 比如使用 dispatch_once(), pthread_once() 或 std::once(). 也就是在第一次使用時(shí)才初始化, 推遲了一部分工作耗時(shí)
main函數(shù)執(zhí)行后
顧名思義就是main函數(shù)開(kāi)始到applicationWillFinishLaunching執(zhí)行完的這個(gè)過(guò)程, 當(dāng)這個(gè)過(guò)程結(jié)束, APP也可以算是完全啟動(dòng)了.
這里只討論整個(gè)啟動(dòng)過(guò)程中的優(yōu)化思路, 至于具體的渲染速度會(huì)專門看一篇來(lái)討論.
在APP啟動(dòng)后, 我們可檢測(cè)代碼執(zhí)行速度的方式, 比如代碼檢測(cè), 或者用xcode自帶的Instruments這里也不多贅述, 我們這次探討的問(wèn)題還是利用現(xiàn)有的條件如何優(yōu)化, 簡(jiǎn)單說(shuō)了, 就是調(diào)整APP啟動(dòng)時(shí)的函數(shù)執(zhí)行順序, 讓用戶的感覺(jué)更流程.
APP啟動(dòng)后我們一般做的事情:
- 初始化SDKs
- 配置APP運(yùn)行需要的環(huán)境
- 自己一些工具類的初始化
- 加載mainTabBarController和mainNavController
- ...
大體也可以把這些事情分為四個(gè)類型:
- 日志, 統(tǒng)計(jì)等基礎(chǔ)功能配置
- 項(xiàng)目配置, 基本信息配置, 推送, IM等配置
- 加載主視圖
- 其它SDKs配置
對(duì)于第一類, 日志, 統(tǒng)計(jì)等, 還是必須第一時(shí)間啟動(dòng), 只能放在didFinishLaunchingWithOptions啟動(dòng), 好像只能這樣.
第二類和第三類, 其實(shí)是主要跟用戶的交互, 其實(shí)優(yōu)先級(jí)是低于第一類的, 而一般APP都有一個(gè)若干秒的廣告頁(yè), 那么我們就可以利用這幾秒的廣告頁(yè)來(lái)做一些事情.
具體就是在didFinishLaunchingWithOptions只加載廣告頁(yè), 讓用戶可以很迅速的看到廣告頁(yè), 然后二和三類的耗時(shí)操作, 在廣告頁(yè)顯示后加載.
第四類的事件, 是優(yōu)先級(jí)最低的, 甚至可以按需等到主視圖didappear后再執(zhí)行, 這樣下來(lái), 其實(shí)在main函數(shù)執(zhí)行后我們只執(zhí)行了第一類事件就讓用戶感受到APP已經(jīng)成功加載, 而二, 三, 四類的事件, 我們則在用戶無(wú)感的情況下進(jìn)行了加載.