前言:
最近帽馋,小編在看戴銘老師的技術(shù)分享搅方,感覺收獲很多≌雷澹基于最近的學(xué)習(xí)姨涡,小編總結(jié)了一些App啟動優(yōu)化上的知識點(diǎn),并計(jì)劃落地一系列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
點(diǎn)擊啟動前,此時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
文件丛楚,打開和讀取這些文件并驗(yàn)證其有效性族壳,接著會找到代碼簽名注冊到內(nèi)核,最后對dylib
的每一個segment
調(diào)用mmap()趣些。
2 . rebase/bind:進(jìn)行rebase
指針調(diào)整和bind
符號綁定决侈。
3 . ObjC setup:runtime運(yùn)行時初始化。包括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ù)的讀确跹印;
首屏渲染的大量計(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í)行一次怎诫。
小知識點(diǎn):
+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ā)高手課》