iOS老司機的App啟動優化Tips, 讓啟動速度提升10%
持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第7天,點選檢視活動詳情
前言
- 評價一個App是不是一款出色的應用, 第一印象很重要.
- 這就要求我們必須把App的啟動速度的優先順序排的很高, 設想一個場景, 點選了App圖示, 3秒過去了, 依然還卡在啟動狀態.這是令使用者難以接受的事.
- 要想解決問題, 首先要對問題有個全面的認識.
- 下面我們就App啟動流程及啟動優化實操層面, 做一個拋磚引玉的探討, 如有錯誤, 請評論區指正, 先行謝過了:)
1. iOS啟動流程分析
- App的啟動可以分為兩種
- 冷啟動(Cold Launch): 從零開始, 點選App圖示啟動App
- 冷啟動又可以概括為3大階段
-
- dyld
-
- runtime
-
- main.m
- 熱啟動(Warm Launch): App之前已經啟動好了, 在記憶體中, 後臺執行, 再次點選App圖示啟動App
- App啟動時間的優化, 主要是針對冷啟動進行.
1.1 列印檢視當前App的啟動時間
- 可以通過新增XCode環境變數打印出App的啟動耗時
- Edit scheme -> Run -> Arguments ,
DYLD_PRINT_STATISTICES
設定為1
.
1.2 App冷啟動, 流程分析
- 啟動時間是使用者點選App圖示, 到第一個介面展示的時間.
- 以main函式作為分水嶺, 啟動時間其實包含了兩部分,
-
- main函式之前, 分析並載入動態庫, 註冊需要的類, Category中的方法也會註冊到對應的類中, 執行必要的初始化
+load
方法
- main函式之前, 分析並載入動態庫, 註冊需要的類, Category中的方法也會註冊到對應的類中, 執行必要的初始化
-
- main函式到第一個介面的
viewDidAppear
.
- main函式到第一個介面的
- 所以, 優化也是從兩個方面進行的, 建議先優化第二部分, 因為絕大多數App的瓶頸在自己的程式碼裡.
2. App冷啟動, 啟動優化策略
2.1 mian函式之前的啟動優化
2.1.1 什麼是dyld?
- dyld(dynamic link editor), 是Apple的動態聯結器, 可以用來裝載
Mach-O
檔案(可執行檔案、動態庫等). - App冷啟動時, dyld所做的事情有哪些?
- 裝載App的可執行檔案, 同時會遞迴載入所有依賴的動態庫.
- 當dyld把可執行檔案、動態庫都裝載完畢後, 會通知
runtime
進行下一步的處理.
2.1.2 dyld層面的優化方向
- 減少動態庫的數量
- 合併動態庫, 比如自己寫的UI控制元件合併成自己的UIKit
- 確認動態庫是
optional
還是required
. -
- 如果該Framework在當前App支援的所有iOS系統版本都存在,
-
- 那麼就設為
required
, 否則就設為optional
, 因為option
會有些額外的檢查.
- 那麼就設為
2.1.3 App冷啟動時runtime都做了哪些事?
- 呼叫
map_images
進行可執行檔案內容的解析和處理 - 在
load_images
中呼叫call_load_methods
, 呼叫所有Class
和Category
的+load
方法 - 進行各種
objc
結構的初始化如註冊Objc類、初始化類物件等 -
呼叫C++靜態初始化器和
__attribute__((constructor))
修飾的函式 -
到此為止, 可執行檔案和動態庫中所有的符號(Class、Protocol、Selector、IMP等)都已經按格式成功載入到記憶體中, 被
runtime
所管理.
2.1.4 runtime載入層面的優化方向
- 合併Category, 如
UIView+Frame
和UIView+AutoLayout
合併成一個. - 將不必須在
+load
方法中做的事, 放到+initialize
中去做.
2.2 main函式之後的啟動優化
- main函式開始執行到顯示出第一個頁面, 這段時間做了哪些事?
- 執行
didFinishLaunchingWithOptions
方法 - 初始化Window, 初始化基礎ViewController
- 獲取資料
- 展示給使用者
- 減少建立執行緒, 執行緒不僅有建立時的時間開銷, 還會消耗記憶體, 每個執行緒大約消耗1kb的記憶體空間.
-
- 執行緒建立的耗時, 區間範圍在4-5毫秒, 建立執行緒後啟動執行緒的耗時區間為5-100毫秒, 平均在29毫秒.
-
- 這是很大的時間開銷, 若在應用啟動時開啟多個執行緒, 則尤為明顯. 多次的上下文切換會帶來開銷.
-
- 在開發中避免濫用多執行緒.
- 合併或者刪減不必要的類/分類/函式, 類越多, 函式越多, 啟動越慢.
- 在設計師可接受的範圍內, 儘量使用小的圖片.
2.3 AppDelegate中的優化
- 從AppDelegate先入手優化
didFinishLaunchingWithOptions
、applicationDidBecomeActive
- 優化的核心思想就是, 能延時的延時, 不能延時的儘量放到後臺去優化.
-
- 日誌、統計等必須在App已啟動就最先配置的事件, 仍留在
didFinishLaunchingWithOptions
裡啟動.
- 日誌、統計等必須在App已啟動就最先配置的事件, 仍留在
-
- 專案配置、環境配置、使用者資訊的初始化、推送、IM等事件, 這些功能在使用者進入App首屏之前是必須要載入完的, 放到開屏廣告頁面的
viewDidAppear
裡.
- 專案配置、環境配置、使用者資訊的初始化、推送、IM等事件, 這些功能在使用者進入App首屏之前是必須要載入完的, 放到開屏廣告頁面的
-
- 其他SDK和配置事件, 由於啟動時間不是必須的, 可以放在首屏的
viewDidAppear
方法裡, 在這裡不會影響啟動時間.
- 其他SDK和配置事件, 由於啟動時間不是必須的, 可以放在首屏的
-
- 每次用NSLog方式列印會隱式的建立一個
Calendar
, 因此需要刪減啟動時各業務的log, 或者僅針對內測版輸出log.
- 每次用NSLog方式列印會隱式的建立一個
-
- 儘量不要在
didFinishLaunchingWithOptions
裡面建立和開啟多執行緒.
- 儘量不要在
3. 總結
- App的啟動由
dyld
主導, 將可執行檔案載入到記憶體, 順便載入所有依賴的動態庫 - 並由
runtiime
負責載入成objc
定義的結構 - 所有初始化工作結束後,
dyld
就會呼叫main
函式 - 接下來就是
UIApplictionMain
函式,AppDelegate
的
3.1 dyld階段
- 減少動態庫、合併一些動態庫定期清理不必要的動態庫
- 減少
Objc
類、分類的數量、減少Selector
數量, 定期清理不必要的類、分類 - 減少C++虛擬函式的數量
3.2 runtime階段
- 用
+initialeze
方法和dispatch_once
取代所有的__attribute__((constructor))
、C++靜態構造器、Objc的+load
方法
3.3 main函式階段
- 在不影響使用者體驗的前提下, 儘可能將一些初始化操作延遲, 不要全部都放在
finishLaunching
方法中, 做到延遲載入, 按需載入.
發文不易, 喜歡點讚的人更有好運氣👍 :), 定期更新+關注不迷路~
ps:歡迎加入筆者18年建立的研究iOS稽核及前沿技術的三千人扣群:662339934,坑位有限,備註“掘金網友”可被群管通過~
「其他文章」
- iOS老司機可落地在中大型iOS專案中的5大接地氣設計模式合集
- iOS老司機的跨端跨平臺Hybrid開發Tips
- iOS老司機的2022年回顧, 聊聊寒冬下的實用<<談判力>>
- iOS老司機可落地的中大型iOS專案中的設計模式優化Tips_橋接模式
- iOS老司機的多執行緒PThread學習分享
- iOS老司機整理, iOSer必會的經典演算法_2
- iOS老司機的<<藍海轉型>>讀書分享
- iOS老司機的<<程式設計師的自我修養:連結、裝載與庫>>讀書分享
- iOS老司機的接地氣演算法Tips
- iOS老司機的RunLoop原理探究及實用Tips
- iOS老司機整理, iOSer必會的經典演算法_1
- iOS老司機的App啟動優化Tips, 讓啟動速度提升10%
- iOS老司機的網路相關Tips
- 戀上資料結構與演算法
- iOS老司機帶你一起把App的崩潰率降到0.1%以下
- 探究Swift的String底層實現
- iOS老司機萬字整理, 可能是最全的Swift Tips
- iOS老司機可落地的中大型iOS專案中的設計模式優化Tips
- 聊一聊Swift中的閉包
- 位元組同學推薦_編寫高質量Objective-C程式碼的52個有效方法