在性能優(yōu)化過(guò)程中,啟動(dòng)速度優(yōu)化一直是一個(gè)比較大的點(diǎn)。Apple 官方期望 APP 的啟動(dòng)時(shí)間為 0.4 s,用戶的期待時(shí)間為 2 s (來(lái)自調(diào)查)。不過(guò)作為開(kāi)發(fā)者,當(dāng)然希望我們的 APP 的啟動(dòng)速度能夠盡可能的快。若是對(duì) Time Profiler 不熟悉的同學(xué)可以先閱讀一下先前的文章 Instruments 之 Time Profiler 使用。
冷啟動(dòng)和熱啟動(dòng)
APP 啟動(dòng)分為冷啟動(dòng)(Cold Launches),當(dāng) APP 長(zhǎng)時(shí)間沒(méi)有被啟動(dòng)的時(shí)候,用戶再次啟動(dòng) APP 的時(shí)候就是冷啟動(dòng),若是手機(jī)重啟之后,APP 的第一次啟動(dòng)也是冷啟動(dòng);冷啟動(dòng)對(duì)應(yīng)的是熱啟動(dòng)(Warm Launches),當(dāng) APP 啟動(dòng)時(shí)需要的 dylibs 仍然停留在設(shè)備的磁盤(pán)緩存的時(shí)候,這個(gè)時(shí)候就是熱啟動(dòng),熱啟動(dòng)的速度會(huì)更快。
優(yōu)化案例
使用 Xcode 版本為 8.3.2 ,設(shè)備為 iPhone 6 ,系統(tǒng)版本 10.3.1。 APP 每次啟動(dòng)之前需要重啟一下手機(jī),達(dá)到冷啟動(dòng)的效果。案例使用 raywenderlich 的
Catstagram 啟動(dòng)優(yōu)化。該案例是一個(gè)帶圖片的列表。

優(yōu)化 before main()

APP 啟動(dòng)優(yōu)化可以分為 2 個(gè)部分,一個(gè)部分在 main() 函數(shù)之前,另一部分在 main()函數(shù)之后。對(duì)于 APP 的啟動(dòng)細(xì)節(jié)可以參考 WWDC 的 Optimizing App Startup Time 章節(jié),本文主要講使用 Time Profiler 來(lái)分析 APP ,然后根據(jù)分析結(jié)果來(lái)優(yōu)化 APP,著重講解 Time Profiler 的使用過(guò)程。
接下來(lái),打開(kāi) Catstagram 案例,添加 Scheme 的 DYLD_PRINT_STATISTICS 參數(shù),并設(shè)置值為 YES,見(jiàn)下圖所示。該DYLD_PRINT_STATISTICS參數(shù)用于讓 Xcode 控制臺(tái)輸出 APP 在 before main() 時(shí)機(jī)之前的花費(fèi)時(shí)間。


設(shè)置好了之后,Command + R 在冷啟動(dòng)情況下啟動(dòng) APP ,可以看到控制臺(tái)的輸出
Total pre-main time: 1.5 seconds (100.0%)
dylib loading time: 814.09 milliseconds (52.6%)
rebase/binding time: 52.20 milliseconds (3.3%)
ObjC setup time: 241.27 milliseconds (15.6%)
initializer time: 437.29 milliseconds (28.3%)
slowest intializers :
libSystem.B.dylib : 19.26 milliseconds (1.2%)
AsyncDisplayKit : 145.63 milliseconds (9.4%)
Catstagram : 277.81 milliseconds (17.9%)
重點(diǎn)關(guān)注 Total pre-main time: 1.5 seconds (100.0%) 的信息,冷啟動(dòng)情況在 pre-main 時(shí)機(jī)中可以看到信息 dylib loading time: 814.09 milliseconds (52.6%) ,也就是說(shuō)我們的 dylib loading time 加載時(shí)間占據(jù)了 52.6% 。聯(lián)想到我們的第三方庫(kù)是采用 pod 管理,并且是 use_frameworks ,frameworks 是一個(gè)可優(yōu)化點(diǎn),
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'Catstagram' do
use_frameworks!
pod 'AFNetworking'
pod 'AsyncDisplayKit', '~> 2.2'
pod 'Yoga', '~> 1.3'
pod 'Firebase', '~> 3.15'
#pod 'FirebaseUI', '~> 3.1'
end

這個(gè) frameworks 是一個(gè)可優(yōu)化點(diǎn),打開(kāi) Podfile,并注釋掉 use_frameworks ,然后命令行執(zhí)行 pod install 命令 ,更新工程,可以看到工程設(shè)置發(fā)生了變化。
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'Catstagram' do
#use_frameworks!
pod 'AFNetworking'
pod 'AsyncDisplayKit', '~> 2.2'
pod 'Yoga', '~> 1.3'
pod 'Firebase', '~> 3.15'
#pod 'FirebaseUI', '~> 3.1'
end

修改好了之后,Command + R 在冷啟動(dòng)情況下啟動(dòng) APP ,查看控制臺(tái)的輸出。
Total pre-main time: 1.1 seconds (100.0%)
dylib loading time: 470.55 milliseconds (39.6%)
rebase/binding time: 31.07 milliseconds (2.6%)
ObjC setup time: 255.35 milliseconds (21.4%)
initializer time: 430.58 milliseconds (36.2%)
slowest intializers :
libSystem.B.dylib : 12.89 milliseconds (1.0%)
Catstagram : 792.04 milliseconds (66.6%)
從 log 中可以看到明顯的變化,Total pre-main time 由之前 1.5 seconds 降到 1.1 seconds 。dylib loading time 由之前的 814.09 milliseconds (52.6%) 降到 470.55 milliseconds (39.6%) 。優(yōu)化效果非常明顯。
優(yōu)化 after main()

優(yōu)化完 before main() 之后,開(kāi)始來(lái)優(yōu)化 after main()。從圖中可以看出啟動(dòng)優(yōu)化的點(diǎn)是集中在 UIApplicationMain()上。
打開(kāi) Instruments 選擇 Time Profiler 來(lái)分析 APP。


選擇 APP 生命周期中的 Launching 生命周期來(lái)分析,如上圖所示,可以清晰的看到耗時(shí)操作主要發(fā)生在 log 操作中,所以我們回到關(guān)于 log 的這段代碼代碼中,它可能是一個(gè)可以?xún)?yōu)化的點(diǎn)。
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
let appearance = UINavigationBar.appearance()
appearance.backgroundColor = .white
appearance.barTintColor = .white
CoolLogger.reportLogs()
return true
}
上述打 log 代碼在 main 線程中執(zhí)行,所以這段代碼是可以?xún)?yōu)化的,我們將這段代碼放到非 main 線程中執(zhí)行。
DispatchQueue.global(qos: .background).async {
CoolLogger.reportLogs()
}
修改代碼之后,重新啟動(dòng)手機(jī),讓 APP 進(jìn)行冷啟動(dòng),繼續(xù)使用 Time Profiler 分析 APP,從下圖的分析結(jié)果總可以看到優(yōu)化取得了效果,APP 的 Laucning 生命周期沒(méi)有出現(xiàn) APP 的生命周期中,說(shuō)明使用了及其短的時(shí)間來(lái)完成啟動(dòng),這時(shí)間長(zhǎng)度可以忽略。

總結(jié)
Time Profiler 可以看到代碼的運(yùn)行時(shí)長(zhǎng),配合它的Lift Cycle 工具可以用來(lái)優(yōu)化 APP 的啟動(dòng)速度。 Time Profiler 只是一個(gè)工具,它只能幫助記錄 APP 的運(yùn)行狀態(tài),而開(kāi)發(fā)者可以根據(jù)記錄的狀態(tài)分析 APP 的耗時(shí)操作,然后進(jìn)行修改,再用 Time Profiler 驗(yàn)證。
參考
本文是 raywenderlich 的課程筆記,內(nèi)容參考 Practical Instruments 課程
1、Demo 項(xiàng)目 https://files.betamax.raywenderlich.com/attachments/videos/786/0965b118-95eb-492f-804c-3135c7347130.zip
2、https://videos.raywenderlich.com/courses/74-practical-instruments/lessons/4