冷啟動 與 熱啟動
- 熱啟動:如果你剛剛啟動過App,這時候App的啟動所需要的數(shù)據仍然在緩存中,再次啟動的時候稱為熱啟動。通常情況下熱啟動能幫助提升啟動速度,但有時也可能會出現(xiàn)app卡死手動退出進程后重新打開仍然是卡死狀態(tài)。
- 冷啟動:如果是比較長時間沒有啟動過app或者設備剛剛重啟,這種情況下啟動App,就被稱為冷啟動。
查看啟動時間
- 最佳速度:400ms,因為不添加任何同步任務從圖標被點擊到顯示Launch Screen,然后Launch Screen消失這段時間就是400ms。如果app啟動時間接近這個數(shù)值,那證明app的啟動任務已經優(yōu)化到最佳。
- 最慢速度:不可以大于20s,否則會被系統(tǒng)殺掉。
配置Xcode環(huán)境變量在日志中打印啟動時間:
打開工程 -> Edit Scheme -> Run -> Environment Variables
根據需要添加DYLD_PRINT_STATISTICS和DYLD_PRINT_STATISTICS_DETAILS環(huán)境變量,1表示Yes,開啟這個功能。

Total pre-main time: 433.19 milliseconds (100.0%)
dylib loading time: 341.79 milliseconds (78.8%)
rebase/binding time: 14.18 milliseconds (3.2%)
ObjC setup time: 35.27 milliseconds (8.1%)
initializer time: 41.79 milliseconds (9.6%)
slowest intializers :
libSystem.B.dylib : 3.40 milliseconds (0.7%)
libMainThreadChecker.dylib : 19.68 milliseconds (4.5%)
libViewDebuggerSupport.dylib : 8.75 milliseconds (2.0%)
優(yōu)化啟動
以main函數(shù)作為分水嶺,啟動時間其實包括了兩部分:main函數(shù)之前和main函數(shù)到第一個界面的viewDidAppear:。
所以,優(yōu)化也是從兩個方面進行的,優(yōu)化效果主要來自于后者,因為絕大多數(shù)App的瓶頸在自己的代碼里。而對于pre-main的優(yōu)化能做的無非是減少不必要的動態(tài)庫引用、多個庫合并成一個,從上面的打印數(shù)據也可以看出,主要耗時是在dylib loading,消耗78.8%的時間。
Main函數(shù)之后
從main函數(shù)開始執(zhí)行,到第一個界面顯示,期間一般做以下任務:
- 執(zhí)行AppDelegate的代理方法,主要是didFinishLaunchingWithOptions
- 初始化Window,初始化基礎的ViewController結構(一般是UINavigationController+UITabViewController+多個UIViewController)
- 獲取數(shù)據(Local DB/Network),展示給用戶。
優(yōu)化:
- 延遲初始化和加載不必要的UIViewController和View。
比方說UITabViewController有四個Item,在啟動的時候盡量只初始化首頁的頁面,其它Item頁面先用空VC占位。而且首頁的內容中不必要的內容也可以先不初始化,做成懶加載形式,在用戶確實需要查看和使用時再初始化。
- 對于確實需要啟動時使用但又比較耗時的事物放倒后臺處理,如果涉及到UI則在處理完成后把刷新任務放回主線程。
日志功能,日志往往涉及到DB操作;
文件讀取,比如讀取本地存儲的省份城市區(qū)縣文件和圖片處理;
大量的計算,比如圖片處理、比較大的json數(shù)據轉Model;
- 能延遲初始化的盡量延遲初始化
三方SDK初始化,比如Crash統(tǒng)計、 像分享之類的,可以等到第一次調用再出初始化。
Main函數(shù)之前
Main函數(shù)之前是iOS系統(tǒng)的工作,所以這部分的優(yōu)化往往更具有通用性。
Pre-Main包含以下工作:
- dylib loading time: 341.79 milliseconds (78.8%)
- rebase/binding time: 14.18 milliseconds (3.2%)
- ObjC setup time: 35.27 milliseconds (8.1%)
- initializer time: 41.79 milliseconds (9.6%)
- slowest intializers :
- libSystem.B.dylib : 3.40 milliseconds (0.7%)
- libMainThreadChecker.dylib : 19.68 milliseconds (4.5%)
- libViewDebuggerSupport.dylib : 8.75 milliseconds (2.0%)
優(yōu)化:
-
loading dylib:啟動的第一步是加載動態(tài)庫,加載系統(tǒng)的動態(tài)庫使很快的,因為可以緩存,而加載內嵌的動態(tài)庫速度較慢。所以,提高這一步的效率的關鍵是:減少動態(tài)庫的數(shù)量。
- 合并動態(tài)庫,比如公司內部由私有Pod建立了如下動態(tài)庫:XXTableView, XXHUD, XXLabel,強烈建議合并成一個XXUIKit來提高加載速度。
-
rebase/binding & ObjC Runtime setup:Rebase和Binding都是為了解決指針引用的問題。對于Objective C開發(fā)來說,主要的時間消耗在Class/Method的符號加載上,所以常見的優(yōu)化方案是:
- 減少
__DATA段中的指針數(shù)量。 - 合并Category和功能類似的類。比如:UIView+Frame,UIView+AutoLayout…合并為一個
- 刪除無用的方法和類。
- 多用Swift Structs,因為Swfit Structs是靜態(tài)分發(fā)的。可以參考Swift進階之內存模型和方法調度
-
Initializers:
用initialize替代load。不少同學喜歡用method-swizzling來實現(xiàn)AOP去做日志統(tǒng)計等內容,強烈建議改為在initialize進行初始化。
load在程序啟動的時候就會調用,而且必須阻塞等著所有類的load方法都執(zhí)行完;initialize在類首次使用的時候調用。減少
__atribute__((constructor))的使用(__attribute__((constructor))用是在main函數(shù)之前,執(zhí)行一個函數(shù),便于我們做一些準備工作)。