App啓動之Dyld在作什麼

前言

這裏主要剖析一下一個App從點擊圖標,到展示首頁的整個過程。數據庫

App是如何啓動的

按順序劃分緩存

  • 加載可執行文件(讀取Mach-O)
  • 加載動態庫(Dylib)
  • Rebase & Bind
  • Objc
  • Initializers

--------- main() ---------安全

  • 執行AppDelegate的代理方法(如:didFinishLaunchingWithOptions)。
  • 根據業務註冊SDK,獲取數據庫數據等。
  • 初始化Windows,初始化ViewController。

加載可執行文件(讀取Mach-O)

Apple操做系統使用dyld加載可執行文件。數據結構

dyld全程爲dynomic loader,做用是加載一個進程所須要的Image,在opensource-apple能夠找到它的開源代碼。架構

加載動態庫(Dylib)

dyld讀取完Mach-O的Header和Load Commands後,就會找到可執行文件的依賴動態庫。接着dyld會將所依賴的動態庫加載到內存中。這是一個遞歸的過程,依賴的動態庫可能還會依賴別的動態庫,因此dyld會遞歸每一個動態庫,直至全部的依賴庫都被加載完畢。app

加載後的動態庫會被緩存到dyld shared cache中,提升讀取效率。dom

簡單的說下Mach-O,簡單的能夠分爲三個部分,Header,Load Commands,Segment Data。函數

Header中包含的是可執行文件的CPU架構,Load Commands的數量和佔用空間。佈局

Load Commands中包含的是Segment的Header與內存分佈,以及依賴動態庫的版本和Path等。優化

Segment Data就是Segment彙編代碼的實現,每段Segment的內存佔用大小都是分頁頁數的整數倍。

iOS-dyld-2019-03-17-3

Rebase & Bind

這兩個過程合在一塊兒說,是由於他們之間的工做是相互補充的。

Apple爲了解決應用安全,用到了ASLR(Address space layout randomization 地址空間佈局隨機化)和Code Sign。

App被啓動後,會被映射到虛擬內存中,這樣App在這個空間中就有了一個起始地址,但這個起始地址是固定的。ASLR能使這個起始地址隨機化,這項技術能夠防止攻擊者經過初始地址+偏移量的方法找到函數的內存地址。

Code Sign就是簽名,在進行加密的時候,會對每個Page(這裏指的是Segment Data)都進行加密,當dyld進行加載的時候,會對每個Page都進行獨立的驗證。

Mach-O中採用了PIC(Position Independent Code 地址無關代碼),大當咱們在調用函數時,會在__Data段中創建一個指向該函數的指針,經過這個指針來間接調用。

Mach-O中有不少符號,有些指向當前Mach-O的(咱們爲App編寫的代碼),有些指向其餘DyLib(依賴的動態庫)。

Rebase的做用是從新修正指向當前Mach-O指針的指向,由於上面提到的ASLR將地址隨機化,起始地址不在是固定的,從新修復後,App才能正常運行。

Bind的做用是從新修復外部指針的指向,這個過程會根據字符串匹配的方式來查找符號表,比起Rebase會略慢(這裏fishhook的實現基礎,它在dyld綁定C庫的時候進行了hook)。

iOS-dyld-2019-03-17-2

Objc

由於Objective C的動態特性,因此在Main函數執行以前,須要把類信息註冊到一個全局Table中。同時,Category的方法也會被註冊到對應類中,Category中的同名方法實現,會根據編譯順序,被最後一個編譯的Category實現所覆蓋。同時還會作Selector的惟一性檢測。

Initializers

這個階段是包含必要的初始化。

  • +load
  • C/C++靜態初始化對象和標記有__attribute__(constructor)的方法

這裏區分下+load方法與+Initialize方法,前者是在類加載時調用的,後者是在類第一次收到message以前調用的。

main()方法以後的事

這裏就不作展開了,都是咱們親手寫的代碼。

Dyld 3

以上咱們介紹了dyld2的加載方式,在2017WWDC,Apple推出了Dyld3。

Dyld2是從程序開始時纔開始執行的,而Dyld3則將Dyld2的一些過程進行了分解。

iOS-dyld-2019-03-17-1

Dyld3分爲out-of-process,和in-process。

out-process會作:

  • 分析Mach-O Headers
  • 分析以來的動態庫
  • 查找須要的Rebase和Bind的符號
  • 將上面的分析結果寫入緩存。

in-process會作:

  • 讀取緩存的分析結果
  • 驗證分析結果
  • 加載Mach-O文件
  • Rebase&Bind
  • Initializers

使用了Dyld3後,App的啓動速度會進一步提升

啓動階段的優化建議

  • 減小動態庫的數量,推薦使用系統庫。
  • 減小類和方法的數量。
  • 減小初始化函數。
  • 儘可能使用Swift。
    • Swift沒有初始化器。
    • Swift不容許特定類型的未對齊數據結構。
    • Swift代碼更精簡。

想要了解Dyld的同窗,能夠看看這篇文章App 啓動流程以及優化 WWDC 2017

參考資料

深刻理解iOS App的啓動過程

App 啓動流程以及優化 WWDC 2017

相關文章
相關標籤/搜索