級別:★☆☆☆☆
標(biāo)簽:「iOS」「啟動流程」「啟動優(yōu)化」
作者: 647
審校: QiShare團(tuán)隊
前言:
最近,小編在看戴銘老師的技術(shù)分享,感覺收獲很多。基于最近的學(xué)習(xí),小編總結(jié)了一些App啟動優(yōu)化上的知識點,并計劃落地一系列App啟動優(yōu)化的文章。
目錄如下:
iOS App啟動優(yōu)化(一)—— 了解App的啟動流程
iOS App啟動優(yōu)化(二)—— 使用“Time Profiler”工具監(jiān)控App的啟動耗時
iOS App啟動優(yōu)化(三)—— 自己做一個工具監(jiān)控App的啟動耗時
本篇將介紹App的兩種啟動方式:“冷/熱啟動”、App完整啟動流程以及“優(yōu)化思路”。
一、“冷啟動”與“熱啟動”
首先,我們先來區(qū)分兩個啟動的概念。
冷啟動:
App點擊啟動前,此時App的進(jìn)程還不在系統(tǒng)里。
需要系統(tǒng)新創(chuàng)建一個進(jìn)程分配給App。(這是一次完整的App啟動過程)熱啟動:
App在冷啟動后用戶將App退回后臺,此時App的進(jìn)程還在系統(tǒng)里。
用戶重新返回App的過程。(熱啟動做的事較少)
主要區(qū)別:
| 名稱 | 區(qū)別 |
|---|---|
| 冷啟動 | 啟動時,App的進(jìn)程不在系統(tǒng)里,需要開啟新進(jìn)程。 |
| 熱啟動 | 啟動時,App的進(jìn)程還在系統(tǒng)里,不需要開啟新進(jìn)程。 |
二、App的完整啟動流程(冷啟動流程)
主要分為三個階段:
main()函數(shù)執(zhí)行前(pre-main階段)main()函數(shù)執(zhí)行后(從main函數(shù)執(zhí)行,到設(shè)置self.window.rootViewController執(zhí)行完成)- 首屏渲染完成后(從
self.window.rootViewController執(zhí)行完成到didFinishLaunchWithOptions方法作用域結(jié)束)
(1)main函數(shù)執(zhí)行前,系統(tǒng)會做的事:
加載可執(zhí)行文件。(
App里的所有.o文件)加載動態(tài)鏈接庫,進(jìn)行
rebase指針調(diào)整和bind符號綁定。ObjC的runtime初始化。
包括:ObjC相關(guān)Class的注冊、category注冊、selector唯一性檢查等。初始化。
包括:執(zhí)行+load()方法、用attribute((constructor))修飾的函數(shù)的調(diào)用、創(chuàng)建C++靜態(tài)全局變量等。

簡單來說,
App啟動后,首先,系統(tǒng)內(nèi)核(Kernel)創(chuàng)建一個進(jìn)程。
其次,加載可執(zhí)行文件。(可執(zhí)行文件是指Mach-O格式的文件,也就是App中所有.o文件的集合體)這時,能獲取到dyld(dyld是蘋果的動態(tài)鏈接器)的路徑。
然后,加載dyld,主要分為4步:
1 . load dylibs:這一階段dyld會分析應(yīng)用依賴的dylib,找到其mach-o文件,打開和讀取這些文件并驗證其有效性,接著會找到代碼簽名注冊到內(nèi)核,最后對dylib的每一個segment調(diào)用mmap()。
2 . rebase/bind:進(jìn)行rebase指針調(diào)整和bind符號綁定。
3 . ObjC setup:runtime運行時初始化。包括ObjC相關(guān)Class的注冊、category注冊、selector唯一性檢查等。
4 . Initializers:調(diào)用每個ObjC類與分類的+load方法,調(diào)用attribute((constructor))修飾的函數(shù)、創(chuàng)建C++靜態(tài)全局變量。
(2)main函數(shù)執(zhí)行后:
main函數(shù)執(zhí)行后的階段,指的是:從 main 函數(shù)執(zhí)行開始,到 appDelegate 的 didFinishLaunchingWithOptions方法里首屏渲染相關(guān)方法執(zhí)行完成。
即,從main函數(shù)執(zhí)行到設(shè)置self.window.rootViewController執(zhí)行完成的階段。
首屏初始化所需配置文件的讀寫操作;
首屏列表大數(shù)據(jù)的讀取;
首屏渲染的大量計算;

(3)首屏渲染完成后:
首屏渲染完成后的階段,指的是:didFinishLaunchingWithOptions方法作用域
內(nèi)執(zhí)行首屏渲染后的所有方法執(zhí)行。
即從設(shè)置self.window.rootViewController到didFinishLaunchWithOptions方法作用域結(jié)束。
這個階段,首屏已經(jīng)渲染完成。
需要做的事:
初始化一些首屏展示不需要的功能。
優(yōu)化主線程,先處理會卡住主線程的方法,不能影響到用戶的后續(xù)操作。
三、具體優(yōu)化思路
用戶能感知到的啟動時長主要是在 “main函數(shù)執(zhí)行前” 、“main函數(shù)執(zhí)行后到首屏渲染完成”的階段。
main函數(shù)執(zhí)行前,優(yōu)化思路如下:
(1)減少使用 +load() 方法
方案一:如果可能的話,將
+load中的內(nèi)容,放到渲染完成后做。方案二:使用
+initialize()的方法代替+load(),注意把邏輯移動到+initialize()時,要注意避免+initialize()的重復(fù)調(diào)用問題,可以使用dispatch_once()讓邏輯只執(zhí)行一次。
小知識點:
+load()與+initialize()兩者的區(qū)別?
+load()方法會在main()函數(shù)調(diào)用前就調(diào)用,而+initialize()是在類第一次使用時才會調(diào)用。
+load方法的調(diào)用優(yōu)先級: 父類 > 子類 > 分類,并且不會被覆蓋,均會調(diào)用。
+load方法是在main() 函數(shù)之前調(diào)用,所有的類文件都會加載,包括分類也會加載。
+initialize方法的調(diào)用優(yōu)先級:分類 > 子類,父類 > 子類。(父類的分類重寫了+initialize方法會覆蓋父類的+initialize方法)
(2)合并多個動態(tài)庫
蘋果公司建議使用更少的動態(tài)庫,并且建議在使用動態(tài)庫的數(shù)量較多時,盡量將多個動態(tài)庫進(jìn)行合并。數(shù)量上,蘋果公司最多可以支持6個非系統(tǒng)動態(tài)庫合并為一個。
(3)優(yōu)化類、方法、全局變量
減少加載啟動后不會去使用的類或方法;少用C++全局變量;
main函數(shù)執(zhí)行后,優(yōu)化方案如下:
(4)優(yōu)化首屏渲染前的功能初始化
main函數(shù)執(zhí)行后到首屏渲染完成前,只處理首屏渲染相關(guān)業(yè)務(wù)。
首屏渲染外的其他功能放到首屏渲染完成后去初始化。
(5)優(yōu)化主線程耗時操作,防止屏幕卡頓。
首先檢查首屏渲染前,主線程上的耗時操作。將耗時操作滯后或異步處理。
通常的耗時操作有:網(wǎng)絡(luò)加載、編輯、存儲圖片和文件等資源。
針對耗時操作做相對應(yīng)的優(yōu)化即可。
最后,我是站在iOS業(yè)界巨人的肩膀上完成了App啟動優(yōu)化(一)、(二)、(三),感謝戴銘老師精彩的技術(shù)分享。
另附上,戴銘老師課程鏈接:《iOS開發(fā)高手課》
推薦文章:
iOS WKWebView的基本使用
Swift 5.1 (4) - 集合類型
iOS 解析一個自定義協(xié)議
iOS13 DarkMode適配(二)
iOS13 DarkMode適配(一)
2019蘋果秋季新品發(fā)布會速覽
申請?zhí)O果開發(fā)者賬號的流程
Sign In With Apple(一)