iOS 中你不會注意到的 Link 過程

一個 app 不可能只是一個單一的可執(zhí)行文件,而是由很多不同的模塊組合而成,這些模塊就是所謂的庫,iOS 中的鏈接(Linking) 是為了鏈接你的應用中會用到的庫。在實際開發(fā)過程中,一個庫一般都包括了可執(zhí)行代碼,公共的頭文件和資源,這些庫可以被鏈接器連接到你的應用。

這些被鏈接的庫可以分為兩種:靜態(tài)庫和動態(tài)庫。

靜態(tài)庫:
其實就是很多目標文件(object file)的壓縮包,當一個靜態(tài)庫被鏈接時,靜態(tài)鏈接器會拿到庫里面的目標文件,把它們與應用的目標文件組合成一個單獨的可執(zhí)行文件。顯而易見,這樣的話,應用的可執(zhí)行文件就會隨添加的庫變得越來越大,當一個應用啟動時,這個應用的代碼會被立刻加載到內存空間里。

動態(tài)庫:
相反,動態(tài)庫僅僅在需要的時候才被加載到內存,這個過程可能發(fā)生在啟動的時候或者運行的時候。

Framework:
在蘋果的世界里,Framework 是一個包含動態(tài)庫,頭文件和資源的包,它的作用是把所有跟庫有關的文件放到一個包里面,方便管理和安裝。

在一個app通過main函數,進入appDelegate的回調函數,開始運行你所寫的類,屬性,方法等等之前,系統(tǒng)已經運用編譯器(compiler)把開發(fā)者們編寫的 .h, .m 源代碼會被轉化成一個個 .o 文件(object file),然后運用靜態(tài)鏈接器(static linker)把這些對象文件組合成最終產品,比如可執(zhí)行代碼或者靜態(tài)庫等等.

靜態(tài)鏈接:
發(fā)生在代碼編譯之后,把各個object file連接成單個二進制文件。靜態(tài)鏈接器(ldld64)解析代碼中對于外部庫的符號(symbol)引用,加入這些symbol在內存中的位置以便于 動態(tài)鏈接器(dyld) 之后可以運用這些位置動態(tài)查找,加載,鏈接。

動態(tài)鏈接:
當app啟動時,內核新建一個進程,在新的進程中加載可執(zhí)行文件,即你寫的程序, 和dynamic linker(dyld),內核在動態(tài)鏈接器中執(zhí)行程序,而動態(tài)鏈接器則會加載程序中引用的庫。

具體地說,動態(tài)鏈接器會依次執(zhí)行以下一些工作:

  1. 在內核分配的進程的最原始的棧里面啟動自己
  2. 遞歸地加載所有的程序中導入的動態(tài)庫到進程的內存空間,動態(tài)鏈接器會緩存這些加載過程
  3. 把這些加載到進程的庫鏈接到可執(zhí)行文件,這一過程中會立即綁定那些需要立即綁定的symbol,并且為那些不需要立即綁定的symbol建立一個表
  4. 為可執(zhí)行文件運行靜態(tài)初始化函數,對于 Objective-C 的類而言,就是 +load 方法,對于 C++ 的類而言就是構造函數
  5. 準備可執(zhí)行文件的 main 函數的參數并且調用 main 函數
  6. 在進程執(zhí)行的過程中, 當可執(zhí)行文件需要調用之前沒有綁定的symbol時,dyld 會通過之前生成的表綁定這些symbol,dyld 也通過 dl 開頭的一些 API 的功能提供運行時加載的服務,提供對 gdb 以及其他 debugger 的掛鉤來獲得有用的信息
  7. main 結束返回之后,運行靜態(tài)終止函數,比如 C++ 中的析構函數
  8. 在一些場景里,當 main 函數返回之后會調用 libSystem's_exit

Reference

MikeAsh博客
蘋果官方文檔

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容