????????啟動(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(),
工具通過(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ù)邏輯在處理。