iOS App啟動優(yōu)化(一)—— 了解App的啟動流程

級別:★☆☆☆☆
標(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的完整啟動流程(冷啟動流程)

主要分為三個階段:

  1. main() 函數(shù)執(zhí)行前(pre-main階段)
  2. main() 函數(shù)執(zhí)行后(從main函數(shù)執(zhí)行,到設(shè)置self.window.rootViewController執(zhí)行完成)
  3. 首屏渲染完成后(從self.window.rootViewController執(zhí)行完成到didFinishLaunchWithOptions方法作用域結(jié)束)

(1)main函數(shù)執(zhí)行前,系統(tǒng)會做的事:
  • 加載可執(zhí)行文件。(App里的所有.o文件)

  • 加載動態(tài)鏈接庫,進(jìn)行rebase指針調(diào)整和bind符號綁定。

  • ObjCruntime初始化。
    包括: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í)行開始,到 appDelegatedidFinishLaunchingWithOptions方法里首屏渲染相關(guān)方法執(zhí)行完成。
即,從main函數(shù)執(zhí)行到設(shè)置self.window.rootViewController執(zhí)行完成的階段。

  • 首屏初始化所需配置文件的讀寫操作;

  • 首屏列表大數(shù)據(jù)的讀取;

  • 首屏渲染的大量計算;

main函數(shù)執(zhí)行后

(3)首屏渲染完成后:

首屏渲染完成后的階段,指的是:didFinishLaunchingWithOptions方法作用域
內(nèi)執(zhí)行首屏渲染后的所有方法執(zhí)行。
即從設(shè)置self.window.rootViewControllerdidFinishLaunchWithOptions方法作用域結(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(一)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容