iOS效能優化 - APP啟動時間優化

Simba_LX發表於2018-08-03

廢話不說,直接上乾貨!


一、APP啟動過程

1.解析Info.plist

  • 載入相關資訊,例如如閃屏
  • 沙箱建立、許可權檢查

2.Mach-O載入

  • 如果是胖二進位制檔案,尋找合適當前CPU類別的部分
  • 載入所有依賴的Mach-O檔案(遞迴呼叫Mach-O載入的方法)
  • 定位內部、外部指標引用,例如字串、函式等
  • 執行宣告為__attribute__((constructor))的C函式
  • 載入類擴充套件(Category)中的方法
  • C++靜態物件載入、呼叫ObjC的 +load 函式

3.程式執行

  • 呼叫main()
  • 呼叫UIApplicationMain()
  • 呼叫applicationWillFinishLaunching

二、監測啟動時間

1.開啟時間分析功能

在Xcode的選單中選擇Project→Scheme→Edit Scheme...,然後找到Run → Environment Variables → +,新增name為DYLD_PRINT_STATISTICSvalue1的環境變數。

iOS效能優化 - APP啟動時間優化

執行後會在控制檯看到如下內容:

Total pre-main time:  94.33 milliseconds (100.0%)
dylib loading time:  61.87 milliseconds (65.5%)
rebase/binding time:   3.09 milliseconds (3.2%)
ObjC setup time:  10.78 milliseconds (11.4%)
initializer time:  18.50 milliseconds (19.6%)
slowest intializers :
libSystem.B.dylib :   3.59 milliseconds (3.8%)
libBacktraceRecording.dylib :   3.65 milliseconds (3.8%)
MyTest :   7.09 milliseconds (7.5%)
複製程式碼

得出結論:

  • main()函式之前總共使用了94.33ms
  • 在94.33ms中,載入動態庫用了61.87ms,指標重定位使用了3.09ms,ObjC類初始化使用了10.78ms,各種初始化使用了18.50ms。
  • 在初始化耗費的18.50ms中,用時最多的三個初始化是libSystem.B.dylib、libBacktraceRecording.dylib以及MyTest。

三、影響APP啟動效能的因素

1.main()函式之前耗時的影響因素

  • 動態庫載入越多,啟動越慢。
  • ObjC類越多,啟動越慢
  • C的constructor函式越多,啟動越慢
  • C++靜態物件越多,啟動越慢
  • ObjC的+load越多,啟動越慢

實驗證明,在ObjC類的數目一樣多的情況下,需要載入的動態庫越多,App啟動就越慢。同樣的,在動態庫一樣多的情況下,ObjC的類越多,App的啟動也越慢。需要載入的動態庫從1個上升到10個的時候,使用者幾乎感知不到任何分別,但從10個上升到100個的時候就會變得十分明顯。同理,100個類和1000個類,可能也很難查察覺得出,但1000個類和10000個類的分別就開始明顯起來。

同樣的,儘量不要寫__attribute__((constructor))的C函式,也儘量不要用到C++的靜態物件;至於ObjC的+load方法,似乎大家已經習慣不用它了。任何情況下,能用dispatch_once()來完成的,就儘量不要用到以上的方法。

2.main()函式之後耗時的影響因素

main()函式開始至didFinishLaunchingWithOptions結束,我們統一稱為main()函式之後的部分。

  • 執行main()函式的耗時
  • 執行didFinishLaunchingWithOptions的耗時
  • rootViewController及其childViewController的載入、view及其subviews的載入

3.didFinishLaunchingWithOptions的耗時

其實 didFinishLaunchingWithOptions 方法裡我們一般都有以下的邏輯:

  • 初始化第三方 SDK
  • 配置 APP 執行需要的環境,比如rootViewController載入
  • 自己的一些工具類的初始化

四、制定優化目標

分為四個部分:

  1. main()函式之前
  2. main()函式之後至didFinishLaunchingWithOptions完成
  3. App完成所有本地資料的載入並將相應的資訊展示給使用者
  4. App完成所有聯網資料的載入並將相應的資訊展示給使用者

五、優化方案

1.main()函式之前的優化

  1. 移除不需要用到的動態庫。
  2. 移除不需要用到的類。
  3. 合併功能類似的類和擴充套件(Category)。
  4. 壓縮資源圖片。
  5. 減少load 方法中執行的程式碼。

2.main()函式之後至didFinishLaunchingWithOptions完成的優化

這一步主要優化didFinishLaunchingWithOptions方法中的程式碼和rootViewController載入的優化。

  1. didFinishLaunchingWithOptions優化
    對於 didFinishLaunchingWithOptions,這裡面的初始化是必須執行的,但是我們可以適當的根據功能的不同對應的適當延遲啟動的時機。對於我們專案,我將初始化分為三個型別:
  • 日誌、統計等必須在 APP 一起動就最先配置的事件

  • 專案配置、環境配置、使用者資訊的初始化 、推送、IM等事件

  • 其他 SDK 和配置事件

對於第一類,由於這類事件的特殊性,所以必須第一時間啟動,仍然把它留在 didFinishLaunchingWithOptions 裡啟動。第二類事件,這些功能在使用者進入 APP 主體的之前是必須要載入完的,所以我們可以把它放在第二批,也就是使用者已經看到廣告頁面,再進行廣告倒數計時的時候再啟動。第三類事件,由於不是必須的,所以我們可以放在第一個介面渲染完成以後的 viewDidAppear 方法裡,這裡完全不會影響到啟動時間。

  1. rootViewController載入優化
    這個只能看具體的邏輯和業務了,儘量把不必要的業務和網路請求等延後載入。

相關文章