iOS啟動(dòng)耗時(shí)分析

????????啟動(dòng)耗時(shí)分析,一般我們會(huì)以main函數(shù)作為分割點(diǎn),main之前和main之后main之前稱為per-main 階段。這個(gè)由dyld給你反饋應(yīng)用的耗時(shí)。main之后由開(kāi)發(fā)者自己檢測(cè)。我們可以從main開(kāi)始打點(diǎn),到第一個(gè)頁(yè)面顯示為止。

通過(guò)實(shí)際的調(diào)試,我們得到各個(gè)函數(shù)的調(diào)用順序如下:

啟動(dòng)頁(yè)

main()

UIApplicationMain()

willFinishLaunchingWithOptions()

didFinishLaunchingWithOptions()

loadView()

viewDidLoad()

applicationDidBecomeActive()

啟動(dòng)頁(yè)是在main()函數(shù)調(diào)用之前出來(lái)的,main()是程序的入口,里面調(diào)用了UIApplicationMain()。當(dāng)App從didFinishLaunchingWithOptions()返回的時(shí)候,實(shí)際的UI立刻開(kāi)始加載,但是在applicationDidBecomeActive()這個(gè)回調(diào)完成之前,UI即使已經(jīng)初始化,但仍舊被阻塞著。

總的啟動(dòng)時(shí)間T包括main()調(diào)用之前的pre-main timeT0,

加上從main()到applicationDidBecomeActive()的時(shí)間T1。

pre-main階段耗時(shí)

?main函數(shù)之前的檢測(cè)蘋果提供了支持,具體配置方式如圖

----首先進(jìn)入Edit Scheme

----然后配置的 key 為:DYLD_PRINT_STATISTICS

----然后我們?cè)龠\(yùn)行項(xiàng)目,該項(xiàng)目 pre-main 的耗時(shí)就會(huì)在控制臺(tái)輸出。

各階段耗時(shí)分析

dylib loading time 動(dòng)態(tài)庫(kù)載入耗時(shí)

載入動(dòng)態(tài)庫(kù),這個(gè)過(guò)程中,會(huì)去裝載app使用的動(dòng)態(tài)庫(kù),而動(dòng)態(tài)庫(kù)之間有它自己的依賴關(guān)系,所以會(huì)消耗時(shí)間去查找和讀取。

優(yōu)化建議:?

1.系統(tǒng)的動(dòng)態(tài)庫(kù),做了優(yōu)化。所以從效率的角度來(lái)說(shuō),盡可能使用系統(tǒng)庫(kù);

2.而對(duì)于開(kāi)發(fā)者定義導(dǎo)入的動(dòng)態(tài)庫(kù)(dynamically linked shared library),則需要在花費(fèi)更多的時(shí)間。Apple官方建議盡量少的使用自定義的動(dòng)態(tài)庫(kù),或者考慮合并多個(gè)動(dòng)態(tài)庫(kù),其中一個(gè)建議是當(dāng)大于6個(gè)的時(shí)候,則需要考慮合并它們;

3.在性能上出發(fā)將動(dòng)態(tài)庫(kù)編譯成靜態(tài)庫(kù)也會(huì)優(yōu)化這部分時(shí)間;

rebase/binding time 修正符號(hào)和綁定符號(hào)耗時(shí)

Rebase:在鏡像(MachO文件)內(nèi)部調(diào)整指針的指向,針對(duì)mach-o在加載到內(nèi)存中不是固定的首地址(ASLR)這一現(xiàn)象做數(shù)據(jù)修正的過(guò)程。

iOS4.3后引入了 ASLR ,MachO會(huì)被加載到隨機(jī)地址,這個(gè)隨機(jī)的地址跟代碼和數(shù)據(jù)指向的舊地址會(huì)有偏差。dyld 需要修正這個(gè)偏差,做法就是將 dylib 內(nèi)部的指針地址都加上這個(gè)偏移量。

binding:將指針指向鏡像(MachO文件)外部的內(nèi)容,binding就是將這個(gè)二進(jìn)制調(diào)用的外部符號(hào)進(jìn)行綁定的過(guò)程。

優(yōu)化建議:

1.核心思想是在進(jìn)行動(dòng)態(tài)庫(kù)的重定位和綁定(Rebase/binding)過(guò)程中減少指針修正;

2.減少Objective-C類數(shù)量,減少分類,減少實(shí)例變量和函數(shù)(刪除不用的類以及冗余代碼,再深一點(diǎn)就是減少第三方工具的使用,可以查看源碼,自己實(shí)現(xiàn));

3.減少C++虛函數(shù);

4.多使用Swift結(jié)構(gòu)體(推薦使用swift)

ObjC setup time OC類注冊(cè)的耗時(shí)

主要做以下幾件事來(lái)完成Objc Setup:

1、讀取二進(jìn)制文件的 DATA 段內(nèi)容,找到與 objc 相關(guān)的信息

2、注冊(cè) Objc 類,ObjC Runtime 需要維護(hù)一張映射類名與類的全局表。當(dāng)加載一個(gè) MachO 時(shí),它定義的所有的類都需要被注冊(cè)到這個(gè)全局表中;

3、讀取 protocol 以及 category 的信息,把category的定義插入方法列表 (category registration),

優(yōu)化建議:

1.不刻意的去減少幾個(gè)類,但是可以避免浪費(fèi);

2 隨著項(xiàng)目的不斷迭代,很多模塊和方法已經(jīng)被廢棄但是卻一直留存在項(xiàng)目中,導(dǎo)致項(xiàng)目越來(lái)越臃腫;

3.我們可以使用一些工具來(lái)查找項(xiàng)目中沒(méi)有被用到的文件。從而達(dá)到優(yōu)化;

initializer time?其他初始化,如上圖,細(xì)分為其他的幾個(gè)部分

1、Objc的+load()函數(shù)

2、C++的構(gòu)造函數(shù)屬性函數(shù) 形如attribute((constructor)) void DoSomeInitializationWork()

優(yōu)化建議:?

1.我們能做的就是將不必須在+load方法中做的事情延遲到+initialize中;

2.這是因?yàn)?load方法是在app啟動(dòng)的時(shí)候就被調(diào)用,而+initialize方法則是在Class第一次使用的時(shí)候才調(diào)用,相當(dāng)于是懶加載了??梢园?load中的代碼移到initialize中,并結(jié)合dispatch_once來(lái)防止重復(fù)調(diào)用;

3.但是我們項(xiàng)目中只有在使用method swizzling的時(shí)候會(huì)在+load中調(diào)用方法。所以這一點(diǎn)也沒(méi)什么好優(yōu)化的;

pre-main階段耗時(shí)總結(jié):

1. 動(dòng)態(tài)庫(kù)加載越多,啟動(dòng)越慢

2. ObjC類,方法越多,啟動(dòng)越慢

3. ObjC的+load越多,啟動(dòng)越慢

4. C的constructor函數(shù)越多,啟動(dòng)越慢

5. C++靜態(tài)對(duì)象越多,啟動(dòng)越慢

main()到applicationDidBecomeActive()的階段耗時(shí)

我們可以使用Xcode自帶工具Instruments里面的Time Profiler來(lái)獲取,也可以在main()的第一句和applicationDidBecomeActive()的最后一句加上獲取時(shí)間的代碼CFAbsoluteTimeGetCurrent(),

Time Profiler

工具通過(guò)Xcode工具欄中Product->Profile(command+i)可以啟動(dòng),(也可以通過(guò)Xcode->Open Developer Tool->Instruments)啟動(dòng)后界面如下:

選擇Time Profiler,打開(kāi)后如圖:

點(diǎn)擊左上角紅色按鈕運(yùn)行,勾選左下角Call Tree中Separate Thread和Hide System Libraries,等到第一個(gè)頁(yè)面顯示出來(lái)的之后,點(diǎn)擊左上角暫停按鈕,下面就會(huì)統(tǒng)計(jì)出每個(gè)步驟的耗時(shí)情況。這個(gè)時(shí)候我們就可以很容易得到啟動(dòng)時(shí)間T1。

針對(duì)這塊時(shí)間的耗時(shí)優(yōu)化總結(jié):

我們通過(guò)Time Profiler拿到每個(gè)步驟的耗時(shí)之后,右下角的 Heaviest Trace 可查看比較消耗CPU的代碼,雙擊點(diǎn)擊進(jìn)去可查看到對(duì)應(yīng)的代碼,進(jìn)行修改。有些操作可以延后執(zhí)行,或者異步執(zhí)行等,這些需要根據(jù)自己的業(yè)務(wù)邏輯在處理。

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

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載自騰訊云 App啟動(dòng)過(guò)程 解析Info.plist加載相關(guān)信息,例如如閃屏沙箱建立、權(quán)限檢查 Mach-O加載...
    乂濫好人閱讀 1,189評(píng)論 0 0
  • 多作者合集,非商業(yè)行為,為自己學(xué)習(xí)鞏固。特此聲明 啟動(dòng)APP的時(shí)候就會(huì)花費(fèi)較長(zhǎng)的時(shí)間,用戶體驗(yàn)很不好。所以針對(duì)AP...
    紅色海_閱讀 851評(píng)論 0 2
  • 一款 App 的啟動(dòng)速度,不單單是用戶體驗(yàn)的事情,往往還決定了它能否獲取更多的用戶。這就好像陌生人第一次碰面,第一...
    vicentwyh閱讀 1,535評(píng)論 0 10
  • 漸變的面目拼圖要我怎么拼? 我是疲乏了還是投降了? 不是不允許自己墜落, 我沒(méi)有滴水不進(jìn)的保護(hù)膜。 就是害怕變得面...
    悶熱當(dāng)乘涼閱讀 4,471評(píng)論 0 13
  • 感覺(jué)自己有點(diǎn)神經(jīng)衰弱,總是覺(jué)得手機(jī)響了;屋外有人走過(guò);每次媽媽不聲不響的進(jìn)房間突然跟我說(shuō)話,我都會(huì)被嚇得半死!一整...
    章魚(yú)的擁抱閱讀 2,369評(píng)論 4 5

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