分析工具:
xCode自帶工具 Instrument 的 Timer Profiler慎冤。
測(cè)試設(shè)備:
- iPhone6 iOS 12.4.8,這臺(tái)設(shè)備相對(duì)來(lái)說(shuō)比較老舊,我們App在次設(shè)備上啟動(dòng)速度非常慢,所以效果較明顯陶夜。
- iphone7 iOS 13.4
- iPhone11 iOS 14.0.1
測(cè)試方式冀泻,從點(diǎn)擊錄制開(kāi)始啟動(dòng)App常侣,到App啟動(dòng)頁(yè)面結(jié)束點(diǎn)擊停止,觀察主線程的耗時(shí)弹渔。
測(cè)試啟動(dòng)結(jié)果
iPhone6 iOS 12.4.8測(cè)試結(jié)果:
可以看到主線程的耗時(shí)一共是6.04秒胳施,start
占用5.71s,_dyld_start
占用327ms(這里需要注意肢专,也因?yàn)椴皇堑谝淮螁?dòng)舞肆,所以dyld
二次加載存在直接從緩存中加載,所以耗時(shí)比第一次啟動(dòng)會(huì)短一些)博杖。
iphone7 iOS 13.4 測(cè)試結(jié)果:
主線程的耗時(shí)一共是1.57秒椿胯,start
占用1.50s,_dyld_start
占用70ms
iPhone11 iOS 14.0.1 測(cè)試結(jié)果:
主線程的耗時(shí)一共是754ms欧募,start
占用642ms压状,_dyld_start
占用111ms
我們看到最長(zhǎng)的時(shí)間是iphone6的iOS12系統(tǒng),6秒的啟動(dòng)時(shí)間是我們絕對(duì)接受不了的跟继,下面我們看一下在main
函數(shù)中都是有那些方法調(diào)研占用了大多數(shù)的時(shí)間。
首先我們選擇左下腳的 Call Tree 按鈕镣丑,選擇隱藏系統(tǒng)方法舔糖,方便我們查看我們自己的方法。
隱藏系統(tǒng)方能后莺匠,只剩下我們代碼的方法列表和耗時(shí)金吗,但是發(fā)現(xiàn)這些方法都是地址,并不是實(shí)際解析之后的方法名趣竣,如圖:
這里需要注意一下需要DSYM解析成對(duì)應(yīng)的方法名摇庙,需要修改一下項(xiàng)目buildSetting的設(shè)置,在對(duì)應(yīng)的編譯模式下遥缕,選擇 DWARF with DYSM File
:
如果啟動(dòng)過(guò)程中有組件的方法卫袒,組件通過(guò)cocoapod集成的,需要在對(duì)應(yīng)的組件也設(shè)置上单匣,可以通過(guò)遍歷全局設(shè)置:
post_install do |installer|
installer.pod_target_subprojects.flat_map { |p| p.targets }.each do |t|
t.build_configurations.each do |config|
# 設(shè)置debug模式夕凝,其他模式類(lèi)似
config.build_settings['DEBUG_INFORMATION_FORMAT'] = 'dwarf-with-dsym'
end
end
end
設(shè)置后需要執(zhí)行一下 pod install
,重新把項(xiàng)目run到手機(jī),通過(guò) Time Profiler查看代碼耗時(shí):
可以看到户秤,在啟動(dòng)過(guò)程中 有三個(gè)計(jì)較耗時(shí)的方法码秉,啟動(dòng) HBHRouterManager.regisgerRootVC(vc:)
方法耗時(shí)達(dá)到5.71秒,我們項(xiàng)目這個(gè)方法是路由注冊(cè)處理鸡号,調(diào)用了全局的類(lèi)列表并組成對(duì)應(yīng)的Scheme鏈接映射表转砖,所以比較耗時(shí)。所以我們要根據(jù)實(shí)際情況優(yōu)化我們耗時(shí)的方法鲸伴,或者調(diào)優(yōu)府蔗,或者延后調(diào)用莉兰,不要堵塞主線程的啟動(dòng)過(guò)程。尤其是在 Appdelegate
的 application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
方法中 在return
之前礁竞,千萬(wàn)不要做特別耗時(shí)的工作堵塞主線程痛黎。可以嘗試在此方法 return
之前 sleep(10)
挑秉,那么你的App啟動(dòng)肯定在10秒以上贤壁。
我們采用簡(jiǎn)單方式先處理一下這個(gè)耗時(shí)的方法:
// 子線程異步執(zhí)行注冊(cè)路由方法,不要堵塞主線程的啟動(dòng)過(guò)程
DispatchQueue.global().async {
HBHRouterManager.shared.regisgerRootVC(vc: rootViewController)
}
重新Run狂男,看一下啟動(dòng)耗時(shí):
可以看到main
和start
調(diào)用時(shí)間直接降低到了 436ms综看,App在iPhone6上啟動(dòng)時(shí)間從原來(lái)的6秒左右降低到739ms。
再看一下剩下的兩臺(tái)機(jī)器:
iphone7 iOS 13.4 測(cè)試結(jié)果:
主線程的耗時(shí)一共是339ms秒岖食,start
占用256ms红碑,_dyld_start
占用72ms
iPhone11 iOS 14.0.1 測(cè)試結(jié)果:
主線程的耗時(shí)一共是146ms,start
占用38ms泡垃,_dyld_start
占用108ms
以此類(lèi)推析珊,其他耗時(shí)的方法采用對(duì)應(yīng)的優(yōu)化方案調(diào)優(yōu),即可實(shí)現(xiàn)最簡(jiǎn)單的啟動(dòng)速度的優(yōu)化蔑穴。
這里只是簡(jiǎn)單介紹在主線程啟動(dòng)執(zhí)行時(shí)的耗時(shí)方法進(jìn)行優(yōu)化忠寻,不同的啟動(dòng)場(chǎng)景對(duì)App的耗時(shí)影響也不太一樣,蘋(píng)果給我們例舉了幾種啟動(dòng)場(chǎng)景:
- 重啟手機(jī)存和,首次啟動(dòng)App奕剃。
- 強(qiáng)制退出App,然后啟動(dòng)捐腿。
- 打開(kāi)其他App纵朋,然后啟動(dòng)你的App。
- 使用一個(gè)非常大的App(例如茄袖,可以使用許多圖形資源或?qū)崟r(shí)攝像機(jī)輸入的App)操软,然后啟動(dòng)您的應(yīng)用程序。
還列舉了其他影響啟動(dòng)速度的因素:
App動(dòng)態(tài)庫(kù)的數(shù)量绞佩,直接影響到dyld加載的時(shí)間寺鸥。
-
靜態(tài)庫(kù)初始化時(shí):
-
C++
靜態(tài)構(gòu)造函數(shù)。 - 在類(lèi)或者分類(lèi)中的
+load
方法品山。 - 標(biāo)有clang屬性
__attribute__((constructor))
的方法 胆建。 - 鏈接到App或Framework任何函數(shù)的
__DATA
、__mod_init_func
肘交。
-
關(guān)于線上版本App的啟動(dòng)時(shí)長(zhǎng)統(tǒng)計(jì)笆载,蘋(píng)果也給提供了工具,可以直接查看不通版本的啟動(dòng)耗時(shí):
Xcode -> Window-> Organizer -> Metrics - > Launch Time
最后附上官網(wǎng)優(yōu)化文檔:蘋(píng)果官網(wǎng)啟動(dòng)優(yōu)化文檔