最近在學(xué)習(xí)戴銘老師的《iOS開發(fā)高手課》,方便后續(xù)開發(fā),特此做筆記記錄一下。
本節(jié)講解的主要內(nèi)容是:App 啟動(dòng)速度怎么做優(yōu)化與監(jiān)控?
啟動(dòng)速度的優(yōu)化必然就是 App 開發(fā)過程中,不可或缺的一個(gè)環(huán)節(jié),那些接下來要分析一下App 在啟動(dòng)時(shí)都做了哪些事兒.
一. App 啟動(dòng)時(shí)都干了哪些事?
一般情況下,App 的啟動(dòng)分為冷啟動(dòng)和熱啟動(dòng).
a.冷啟動(dòng)是指, App 點(diǎn)擊啟動(dòng)前,它的進(jìn)程不在系統(tǒng)里,需要系統(tǒng)新創(chuàng)建一個(gè)進(jìn)程分配給它啟動(dòng)的情況。這是一次完整的啟動(dòng)過程。
b.熱啟動(dòng)是指 ,App 在冷啟動(dòng)后用戶將 App 退后臺(tái),在 App 的進(jìn)程還在系統(tǒng)里的情況下,用戶重新啟動(dòng)進(jìn)入 App的過程,這個(gè)過程做的事情非常少。
今天學(xué)習(xí)的這篇文章戴銘老師只針對冷啟動(dòng)展開了討論。
一般而言,App 的啟動(dòng)時(shí)間,指的是從用戶點(diǎn)擊 App 開始,到用戶看到第一個(gè)界面之間的時(shí)間??偨Y(jié)來說,App 的啟動(dòng)主要包括三個(gè)階段:
1.main() 函數(shù)執(zhí)行前;
2.main() 函數(shù)執(zhí)行后;
3.首屏渲染完成后。
?? main() 函數(shù)執(zhí)行前,系統(tǒng)主要會(huì)做以下幾件事情:
a.加載可執(zhí)行文件(App 的.o 文件的集合);
b.加載動(dòng)態(tài)鏈接庫,運(yùn)行rebase指針調(diào)整和bind符號綁定;
c.Objc 運(yùn)行時(shí)的初始處理,包括 Objc 相關(guān)類的注冊、category 注冊、selector 唯一性檢查等;
d.初始化,包括了執(zhí)行 +load() 方法、attribute((constructor)) 修飾的函數(shù)的調(diào)用、創(chuàng)建 C++ 靜態(tài)全局變量。
?? 在這個(gè)階段對于啟動(dòng)速度優(yōu)化來說,可以做的事情包括:
a.減少動(dòng)態(tài)庫加載。每個(gè)庫本身都有依賴關(guān)系,蘋果公司建議使用更少的動(dòng)態(tài)庫,并且建議在使用動(dòng)態(tài)庫的數(shù)量較多時(shí),盡量將多個(gè)動(dòng)態(tài)庫進(jìn)行合并。數(shù)量上,蘋果公司最多可以支持 6 個(gè)非系統(tǒng)動(dòng)態(tài)庫合并為一個(gè)。
b.減少加載啟動(dòng)后不會(huì)去使用的類或者方法。
c.+load() 方法里的內(nèi)容可以放到首屏渲染完成后再執(zhí)行,或使用 +initialize() 方法替換掉。因?yàn)椋谝粋€(gè)+load() 方法里,進(jìn)行運(yùn)行時(shí)方法替換操作會(huì)帶來 4毫秒的消耗。不要小看這 4 毫秒,積少成多,執(zhí)行 +load() 方法對啟動(dòng)速度的影響會(huì)越來越大。
d.控制 C++ 全局變量的數(shù)量。
?? main() 函數(shù)執(zhí)行后的階段,指的是從 main() 函數(shù)執(zhí)行開始,到 appDelegate 的 didFinishLaunchingWithOptions 方法里首屏渲染相關(guān)方法執(zhí)行完成。
首頁的業(yè)務(wù)代碼都是要在這個(gè)階段,也就是首屏渲染前執(zhí)行的,主要包括了:
a.首屏初始化所需配置文件的讀寫操作;
b.首屏列表大數(shù)據(jù)的讀取;
c.首屏渲染的大量計(jì)算等。
?? 在這個(gè)階段對于啟動(dòng)速度優(yōu)化來說,可以做的事情應(yīng)該是從功能上梳理出哪些是首屏渲染必要的初始化功能,哪些是 App 啟動(dòng)必要的初始化功能,而哪些是只需要在對應(yīng)功能開始使用時(shí)才需要初始化的。梳理完之后,將這些初始化功能分別放到合適的階段進(jìn)行。
?? 首屏渲染完成后這個(gè)階段主要完成的是,非首屏其他業(yè)務(wù)服務(wù)模塊的初始化、監(jiān)聽的注冊、配置文件的讀取等。從函數(shù)上來看,這個(gè)階段指的就是截止到didFinishLaunchingWithOptions 方法作用域內(nèi)執(zhí)行首屏渲染之后的所有方法執(zhí)行完成。簡單說的話,這個(gè)階段就是從渲染完成時(shí)開始,到 didFinishLaunchingWithOptions 方法作用域結(jié)束時(shí)結(jié)束。
這個(gè)階段用戶已經(jīng)能夠看到 App 的首頁信息了,??所以優(yōu)化的優(yōu)先級排在最后。但是,那些會(huì)卡住主線程的方法還是需要最優(yōu)先處理的,不然還是會(huì)影響到用戶后面的交互操作。
以上就是App啟動(dòng)階段需要完成的工作,我們就可以有的放矢地進(jìn)行啟動(dòng)速度的優(yōu)化了。
這些優(yōu)化,包括了功能級別和方法級別的啟動(dòng)優(yōu)化。接下來,我們就從這兩個(gè)角度展開看看。
?? 功能級別的啟動(dòng)優(yōu)化,就是要從main() 函數(shù)執(zhí)行后這個(gè)階段下手。
?? 優(yōu)化的思路是: main() 函數(shù)開始執(zhí)行后到首屏渲染完成前只處理首屏相關(guān)的業(yè)務(wù),其他非首屏業(yè)務(wù)的初始化、監(jiān)聽注冊、配置文件讀取等都放到首屏渲染完成后去做。
?? 方法級別的啟動(dòng)優(yōu)化,檢查首屏渲染完成前主線程上有哪些耗時(shí)方法,將沒必要的耗時(shí)方法滯后或者異步執(zhí)行。通常情況下,耗時(shí)較長的方法主要發(fā)生在計(jì)算大量數(shù)據(jù)的情況下,具體的表現(xiàn)就是就是加載、編輯、存儲(chǔ)圖片和文件等資源。
二.對App啟動(dòng)速度的監(jiān)控,主要有兩種手段。
?? 第一種方法是,定時(shí)抓取主線程上的方法調(diào)用堆棧,計(jì)算一段時(shí)間里各個(gè)方法的耗時(shí)。
Xcode 工具套件里自帶的 Time Profiler ,采用的就是這種方式。
優(yōu)點(diǎn):開發(fā)類似工具成本不高,能夠快速開發(fā)后集成到你的 App 中,以便在真實(shí)環(huán)境中進(jìn)行檢查。
缺點(diǎn):定時(shí)間隔設(shè)置得長了,會(huì)漏掉一些方法,從而導(dǎo)致檢查出來的耗時(shí)不精確;而定時(shí)間隔設(shè)置得短了,抓取堆棧這個(gè)方法本身調(diào)用過多也會(huì)影響整體耗時(shí),導(dǎo)致結(jié)果不準(zhǔn)確。
這個(gè)定時(shí)間隔如果小于所有方法執(zhí)行的時(shí)間(比如 0.002 秒),那么基本就能監(jiān)控到所有方法。但這樣做的話,整體的耗時(shí)時(shí)間就不夠準(zhǔn)確。一般將這個(gè)定時(shí)間隔設(shè)置為 0.01 秒。這樣設(shè)置,對整體耗時(shí)的影響小,不過很多方法耗時(shí)就不精確了。但因?yàn)檎w耗時(shí)的數(shù)據(jù)更加重要些,單個(gè)方法耗時(shí)精度不高也是可以接受的,所以這個(gè)設(shè)置也是沒問題的。
總結(jié)來說,定時(shí)抓取主線程調(diào)用棧的方式雖然精準(zhǔn)度不夠高,但也是夠用的。
?? 第二種方法是,對objc_msgSend 方法進(jìn)行 hook 來掌握所有方法的執(zhí)行耗時(shí)。
hook 方法的意思是,在原方法開始執(zhí)行時(shí)換成執(zhí)行其他你指定的方法,或者在原有方法執(zhí)行前后執(zhí)行你指定的方法,來達(dá)到掌握和改變指定方法的目的。
hook objc_msgSend 這種方式的優(yōu)點(diǎn)是非常精確,而缺點(diǎn)是只能針對 Objective-C 的方法。當(dāng)然,對于 c 方法和 block 也不是沒有辦法,你可以使用 libffi 的 ffi_call 來達(dá)成 hook,但缺點(diǎn)就是編寫維護(hù)相關(guān)工具門檻高。
綜上,如果對于檢查結(jié)果精準(zhǔn)度要求高的話,我比較推薦你使用 hook objc_msgSend 方式來檢查啟動(dòng)方法的執(zhí)行耗時(shí)。
小結(jié)
啟動(dòng)速度優(yōu)化和監(jiān)控的重要性不言而喻,加快 App 的啟動(dòng)速度對用戶的體驗(yàn)提升是最大的。
啟動(dòng)速度的優(yōu)化也有粗有細(xì):粗上來講,這需要對啟動(dòng)階段功能進(jìn)行分類整理,合理地將和首屏無關(guān)的功能滯后,放到首屏渲染完成之后,保證大頭兒沒有問題;細(xì)的來講,這就需要些匠人精神,使用合適的工具,針對每個(gè)方法進(jìn)行逐個(gè)分析、優(yōu)化,每個(gè)階段都做到極致。
<iOS開發(fā)高手課>之第一講
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
相關(guān)閱讀更多精彩內(nèi)容
- 前言:本文簡單描述APP啟動(dòng)過程和監(jiān)控,一些深入原理性的東西可能需要繞路了,站在大神的肩膀上,簡單總結(jié)跟APP啟動(dòng)...
- 背景 一個(gè)項(xiàng)目做的時(shí)間長了,啟動(dòng)流程往往容易雜亂,庫也用的越來越多,APP的啟動(dòng)時(shí)間也會(huì)慢慢變長。本次將針對iOS...
- 愛情就是死循環(huán),一旦執(zhí)行就陷進(jìn)去了;愛上一個(gè)人,就是內(nèi)存泄漏–你永遠(yuǎn)釋放不了;真正愛上一個(gè)人的時(shí)候,那就是常量限定...
- 1、SCSS和Sass Sass 和 SCSS 其實(shí)是同一種東西,我們平時(shí)都稱之為 Sass。他們都是用Ruby開...