啓動時間
啓動時間可謂是用戶對你的App的第一印象,用戶好不容易下載了App,然後饒有興致的開打App,啓動時間過長很可能會讓用戶直接把App打入冷宮。就算用戶非常有耐心,蘋果的watch dog機制也會kill掉啓動時間過長的App,這種情況下給用戶的感覺就是這App怎麼一啓動就卡死然後崩潰了,然後無情卸載。這裏還要說一下,Xcode在debug模式下是沒有開啓watch dog的,所以不要以爲調試時候沒問題就真的沒問題了,一定要在真機上測試一下。
首先我們瞭解一下App的啓動流程
通過實際的調試,我們得到各個函數的調用順序如下:
- 啓動頁
- main()
- UIApplicationMain()
- willFinishLaunchingWithOptions()
- didFinishLaunchingWithOptions()
- loadView()
- viewDidLoad()
- applicationDidBecomeActive()
啓動頁是在main()
函數調用之前出來的,main()
是程序的入口,裏面調用了UIApplicationMain()
。當App從didFinishLaunchingWithOptions()
返回的時候,實際的UI立刻開始加載,但是在applicationDidBecomeActive()
這個回調完成之前,UI即使已經初始化,但仍舊被阻塞着。
總的啓動時間T包括main()
調用之前的pre-main time
T1,加上從main()
到applicationDidBecomeActive()
的時間T2。
獲取啓動時間
我們可以通過環境變量的方法來獲取pre-main time
。打開Xcode->Product->Scheme->Edit Scheme
或者直接command+shift+<
(在鍵盤上是逗號,按住shift就是小於號了)。在Edit Scheme
中添加DYLD_PRINT_STATISTICS
這個環境變量,如果要打印詳細的時間分佈,可以將value設爲1
運行項目之後就會在控制檯會打印出每個階段都耗時多少
dylib loading time:
加載動態庫
rebase/binding time:
修正指針和數據。此外,因爲Objc是一種動態語言,因此需要註冊類名與類相關信息的一張註冊表,對在其他dylib中定義的category,也需要通過rebasing和binding來修正擴展方法的地址來保證selector的唯一性。關於rebase/binding 這塊,有興趣的小夥伴可以看看這篇文章
ObjC setup time:
ObjC類初始化
initializer time:
其他初始化,如上圖,細分爲其他的幾個部分
libMainThreadChecker:
debug時候檢查線程的
瞭解完畢mian()函數之前加載的步驟後,我們可以簡單的分析出影響T1時間的各種因素:
1. 動態庫加載越多,啓動越慢
2. ObjC類,方法越多,啓動越慢
3. ObjC的+load越多,啓動越慢
4. C的constructor函數越多,啓動越慢
5. C++靜態對象越多,啓動越慢
我們已經獲取到了main()
函數之前的啓動時間T1,至於T2,我們可以使用Xcode自帶工具Instruments裏面的Time Profiler來獲取,也可以在main()
的第一句和applicationDidBecomeActive()
的最後一句加上獲取時間的代碼CFAbsoluteTimeGetCurrent()
,這裏就不細說了。
Time Profiler
工具通過Xcode工具欄中Product->Profile(command+i)
可以啓動,(也可以通過Xcode->Open Developer Tool->Instruments
)啓動後界面如下:
選擇Time Profiler
,打開後如圖:
點擊左上角紅色按鈕運行,勾選左下角Call Tree
中Separate Thread
和Hide System Libraries
,等到第一個頁面顯示出來的之後,點擊左上角暫停按鈕,下面就會統計出每個步驟的耗時情況。這個時候我們就可以很容易得到啓動時間T2。
優化啓動時間
針對T1的各個階段分別進行優化處理
1.dylib loading time
- 核心思想是減少dylibs的引用
- 合併現有的dylibs
- 使用靜態庫
2.rebase/binding time
- 核心思想是在進行動態庫的重定位和綁定(Rebase/binding)過程中減少指針修正;
- 減少Objective-C類數量,減少分類,減少實例變量和函數(刪除不用的類以及冗餘代碼,再深一點就是減少第三方工具的使用,可以查看源碼,自己實現);
- 減少C++虛函數;
- 多使用Swift結構體(推薦使用swift)
3.ObjC setup time
核心思想同上,主要是類的註冊,分類的註冊,唯一Selector。這部分內容基本上在上一階段優化過後就不會太過耗時。
4.initializer time
- 使用initialize替代load方法
- 減少使用c/c++的attribute((constructor));推薦使用dispatch_once(),pthread_once(), std:once()等方法
- 不要在初始化中創建線程
- 推薦使用swift
針對T2進行優化
我們通過Time Profiler拿到每個步驟的耗時之後,右下角的 Heaviest Trace 可查看比較消耗CPU的代碼,雙擊點擊進去可查看到對應的代碼,進行修改。有些操作可以延後執行,或者異步執行等,這些需要根據自己的業務邏輯在處理。