App啓動的三個階段:
- main()函數執行前;
- main()函數執行後;
- 首屏渲染完成後。
main()函數執行前
- 加載可執行文件
- 加載動態鏈接庫
- Objc運行時初始處理,相關類的註冊、category註冊、selector唯一性檢查等
- 初始化,執行+load()方法、__attribute__((constructor))修飾的函數調用、創建c++靜態全局變量
此階段優化方案:
- 減少動態庫加載 (可以將多個動態庫合併,非系統動態庫最多6個合爲一個)
- 減少類、分類、方法(selector)的數量
- 用+initialize方法和dispatch_once取代所有的ObjC的+load
- 減少c++全局變量的數量
main()函數執行後
此階段是指main()函數開始執行 -> didFinishLaunchingWithOptions裏首屏渲染方法執行完成
注意首頁業務代碼是在首屏渲染完成前執行
- 首屏相關基礎庫初始化
- 首屏相關業務數據讀取和處理
- 首屏渲染的大量計算
此階段優化方案
- 各種初始化工作,不要都放到此階段裏面,會導致首屏渲染滯後。啓動必要初始化,首屏渲染必要初始化,可以放在此階段。其他的初始化放到對應功能使用之前。
首屏渲染完成後
此階段是指 didFinishLaunchingWithOptions 方法作用域內首屏渲染之後的所有方法執行完成
非首屏其他業務服務模塊的初始化
- 監聽的註冊
- 配置文件的讀取等
- 此階段首頁信息已經顯示完成
此階段優化方案
- 主要優化主線程耗時方法,滯後執行或者異步執行,因爲此階段已經顯示首頁,主要優化用戶的交互。
App啓動速度的監控
通過在工程的scheme中添加環境變量DYLD_PRINT_STATISTICS,設置值爲1,App啓動加載時Xcode的控制檯就會有pre-main各個階段的詳細耗時輸出。
另外兩種方法:
第一種方法:定時抓取主線程上的方法調用堆棧,計算一段時間裏各個方法的耗時
Time Profiler 就是採用這種方式。
注意點
定時間隔長則會漏掉某些耗時短的方法
定時間隔短,抓取堆棧的方法本身會頻繁調用,影響整體耗時
一般設置0.01秒,對整體耗時影響小,但是檢測的很多方法耗時就不準確了。不過整體耗時數據比較重要,單個耗時不準可以接受。
如果設置0.002秒基本所有方法都可以檢測,整體耗時就不準了。
第二種方法:對 objc_msgSend 方法進行hook來掌握所有方法的耗時