前文已經(jīng)通過源碼分析以及實(shí)戰(zhàn)演練帶領(lǐng)讀者學(xué)習(xí)了APP啟動(dòng)的整個(gè)過程,既然知識(shí)已經(jīng)學(xué)了,就要學(xué)以致用。
APP啟動(dòng)-優(yōu)化總結(jié):
main()函數(shù)之前的優(yōu)化:
1、減少動(dòng)態(tài)庫(kù)加載。每個(gè)庫(kù)本身都有依賴關(guān)系,蘋果公司建議使用更少的動(dòng)態(tài)庫(kù),并且建議 在使用動(dòng)態(tài)庫(kù)的數(shù)量較多時(shí),盡量將多個(gè)動(dòng)態(tài)庫(kù)進(jìn)行合并。數(shù)量上,蘋果公司最多可以支 持 6 個(gè)非系統(tǒng)動(dòng)態(tài)庫(kù)合并為一個(gè)。
2、將動(dòng)態(tài)庫(kù)轉(zhuǎn)換成靜態(tài)庫(kù)。
3、進(jìn)行代碼瘦身,合并或刪除無(wú)效的ObjC類、Category、方法、C++ 靜態(tài)全局變量等。
4、+load() 方法里的內(nèi)容可以放到首屏渲染完成后再執(zhí)行,或使用 +initialize() 方法替換 掉。因?yàn)?,在一個(gè) +load() 方法里,進(jìn)行運(yùn)行時(shí)方法替換操作會(huì)帶來(lái) 4 毫秒的消耗。不 要小看這 4 毫秒,積少成多,執(zhí)行 +load() 方法對(duì)啟動(dòng)速度的影響會(huì)越來(lái)越大。
5、控制 C++ 全局變量的數(shù)量。
6、編譯期clang插樁優(yōu)化。
這是main()函數(shù)之前的優(yōu)化,那么main()函數(shù)之后的優(yōu)化要怎么做呢?
其實(shí)這個(gè)部分才是重點(diǎn)中的重點(diǎn),因?yàn)榇蟛糠諥PP只要對(duì)main()函數(shù)后的時(shí)間做好優(yōu)化就能滿足400ms的啟動(dòng)要求。而且這部分的優(yōu)化更貼合大眾,相對(duì)也更簡(jiǎn)單。
main() 函數(shù)執(zhí)行后的階段,指的是從 main() 函數(shù)執(zhí)行開始,到 appDelegate 的
didFinishLaunchingWithOptions 方法里首屏渲染相關(guān)方法執(zhí)行完成。 首頁(yè)的業(yè)務(wù)代碼都是要在這個(gè)階段,也就是首屏渲染前執(zhí)行的,主要包括了:
首屏初始化所需配置文件的讀寫操作; 首屏列表大數(shù)據(jù)的讀取; 首屏渲染的大量計(jì)算等。
很多時(shí)候,開發(fā)者會(huì)把各種初始化工作都放到這個(gè)階段執(zhí)行,導(dǎo)致渲染完成滯后。更加優(yōu)化 的開發(fā)方式,應(yīng)該是從功能上梳理出哪些是首屏渲染必要的初始化功能,哪些是 App 啟動(dòng) 必要的初始化功能,而哪些是只需要在對(duì)應(yīng)功能開始使用時(shí)才需要初始化的。梳理完之后, 將這些初始化功能分別放到合適的階段進(jìn)行。
1、功能級(jí)別的啟動(dòng)優(yōu)化
優(yōu)化的思路是: main() 函數(shù)開始執(zhí)行后到首屏渲染完成前只處理首屏相關(guān)的業(yè)務(wù),其他非 首屏業(yè)務(wù)的初始化、監(jiān)聽注冊(cè)、配置文件讀取等都放到首屏渲染完成后去做。
具體可以考慮從這些角度優(yōu)化:
1、用純代碼的方式,而不是 xib/Storyboard,來(lái)加載首頁(yè)視圖
2、延遲暫時(shí)不需要的二方/三方庫(kù)加載;
3、延遲執(zhí)行部分業(yè)務(wù)邏輯和 UI 配置;
4、延遲加載/懶加載部分視圖;
5、避免首屏加載時(shí)大量的本地/網(wǎng)絡(luò)數(shù)據(jù)讀??;
6、在 release 包中移除 NSLog 打??;
7、在視覺可接受的范圍內(nèi),壓縮頁(yè)面中的圖片大小;
……
如果首屏為 H5 頁(yè)面,針對(duì)它的優(yōu)化,參考VasSonic的原理,可以從這幾個(gè)角度入手:
終端耗時(shí)
webView 預(yù)加載:在 App 啟動(dòng)時(shí)期預(yù)先加載了一次 webView,通過創(chuàng)建空的 webView,預(yù)先啟動(dòng) Web 線程,完成一些全局性的初始化工作,對(duì)二次創(chuàng)建 webView 能有數(shù)百毫秒的提升。
頁(yè)面耗時(shí)(靜態(tài)頁(yè)面)
靜態(tài)直出:服務(wù)端拉取數(shù)據(jù)后通過 Node.js 進(jìn)行渲染,生成包含首屏數(shù)據(jù)的 HTML 文件,發(fā)布到 CDN 上,webView 直接從 CDN 上獲取;
離線預(yù)推:使用離線包。
頁(yè)面耗時(shí)(經(jīng)常需要?jiǎng)討B(tài)更新的頁(yè)面)
并行加載:WebView 的打開和資源的請(qǐng)求并行;
動(dòng)態(tài)緩存:動(dòng)態(tài)頁(yè)面緩存在客戶端,用戶下次打開的時(shí)候先打開緩存頁(yè)面,然后再刷新;
動(dòng)靜分離:將頁(yè)面分為靜態(tài)模板和動(dòng)態(tài)數(shù)據(jù),根據(jù)不同的啟動(dòng)場(chǎng)景進(jìn)行不同的刷新方案;
預(yù)加載:提前拉取需要的增量更新數(shù)據(jù)。
2、方法級(jí)別的啟動(dòng)優(yōu)化
第一種方法是,定時(shí)抓取主線程上的方法調(diào)用堆棧,計(jì)算一段時(shí)間里各個(gè)方法的耗時(shí)。Xcode 工具套件里自帶的 Time Profiler ,采用的就是這種方式。
第二種方法是,對(duì) objc_msgSend 方法進(jìn)行 hook 來(lái)掌握所有方法的執(zhí)行耗時(shí)。