iOS 性能優(yōu)化-App啟動優(yōu)化

場景

假設(shè)一個這樣的場景,早高峰趕公交,沒帶公交卡,掏出手機打開App1準備掃碼上車,結(jié)果App半天進不去,后面的人都怒視著你,然后果斷打開App2,秒開,那么下一次你會選擇哪個App呢,所以說App的啟動速度不僅決定了用戶體驗,更是決定了它是否能過贏得更多客戶

App啟動時都做了啥呢

啟動的方式
  • 冷啟動:從零開始啟動App
  • 熱啟動:App已經(jīng)在內(nèi)存中,后臺存活著,用戶重新啟動進入App的過程,該過程做的事情非常少,啟動很快

優(yōu)化主要從冷啟動的角度出發(fā),主要分為main函數(shù)執(zhí)行前main函數(shù)執(zhí)行后

圖片.png

main函數(shù)執(zhí)行之前

  1. dyld(全名 dynamic link editor Apple的動態(tài)鏈接器,可以用來加載Mach-O文件) 裝載App的可執(zhí)行文件,同時遞歸加載所有依賴的動態(tài)庫
  2. dyld文件將可執(zhí)行文件和動態(tài)庫加載完畢后,通知RunTime進行下一步處理
  3. RunTime做的事情有
    3.1. 調(diào)用map_images進行可執(zhí)行文件內(nèi)容的解析和處理
    3.2. 在load_images中調(diào)用call_load_methods調(diào)用類class和分類category+load方法
    3.3. 進行各種objc結(jié)構(gòu)的初始化(注冊objc類,初始化類結(jié)構(gòu))
    3.4. 調(diào)用C++靜態(tài)初始化器和__attribute__((constructor))修飾的函數(shù)
    4.到此為止,可執(zhí)行文件和動態(tài)庫中所有的符號(Class , Protocol, Selector, IMP,...)都按格式加載到內(nèi)存中被RunTime管理

優(yōu)化方案

  • 減少動態(tài)庫、合并一些動態(tài)庫(定期清理不必要的動態(tài)庫)
  • 減少Objc類、分類的數(shù)量、減少Selector數(shù)量(定期清理不必要的類分類)
  • 減少C++虛函數(shù)數(shù)量
  • Swift盡量使用struct
  • + initialize方法和dispatch_once取代所有的__attribute__((constructor))C++靜態(tài)編譯器、Objc+load方法

main函數(shù)執(zhí)行之后

  1. main()函數(shù)執(zhí)行開始,到 appDelegatedidFinishLaunchingWithOptions 方法里首屏渲染相關(guān)方法執(zhí)行完成階段
  • 首屏初始化所需配置文件的讀寫操作;
  • 首屏列表大數(shù)據(jù)的讀??;
  • 首屏渲染的大量計算等。
    優(yōu)化: 功能上梳理出哪些是首屏渲染必要的初始化功能,哪些是 App 啟動必要的初始化功能,而哪些是只需要在對應(yīng)功能開始使用時才需要初始化的。梳理完之后,將這些初始化功能分別放到合適的階段進行
  1. appDelegatedidFinishLaunchingWithOptions 方法作用域內(nèi)執(zhí)行首屏渲染之后的所有方法執(zhí)行完成階段
  • 非首屏其他業(yè)務(wù)服務(wù)模塊的初始化、監(jiān)聽的注冊、配置文件的讀取等
  • 第三方SDK初始化
    優(yōu)化:
    main()函數(shù)開始執(zhí)行后到首屏渲染完成前只處理首屏相關(guān)的業(yè)務(wù),其他非首屏業(yè)務(wù)的初始化、監(jiān)聽注冊、配置文件讀取等都放到首屏渲染完成后去做.
    將沒必要的耗時方法滯后或者異步執(zhí)行

啟動時間獲取

  • 通過添加環(huán)境變量可以打印出App的啟動時間(Edit scheme -> Run -> Arguments -> Environment VariablesDYLD_PRINT_STATISTICS設(shè)置為1
Total pre-main time: 5.6 seconds (100.0%)
         dylib loading time: 4.3 seconds (76.4%)
        rebase/binding time: 1.1 seconds (20.3%)
            ObjC setup time: 107.58 milliseconds (1.8%)
           initializer time:  71.88 milliseconds (1.2%)
           slowest intializers :
             libSystem.B.dylib :  22.57 milliseconds (0.3%)

打印的是執(zhí)行main函數(shù)之前的耗時

  • Time Profiler定時抓取主線程上的方法調(diào)用堆棧,計算一段時間里各個方法的耗時
  • fishhook https://github.com/facebook/fishhook
  • 戴銘的GCDFetchFeed 在需要檢測耗時時間的地方調(diào)用[SMCallTrace start],結(jié)束時調(diào)用 stopsave就可以打印出方法的調(diào)用層級和耗時了。你還可以設(shè)置最大深度和最小耗時檢測,來過濾不需要看到的信息
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容