一個 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)鏈接器(ld 和 ld64)解析代碼中對于外部庫的符號(symbol)引用,加入這些symbol在內存中的位置以便于 動態(tài)鏈接器(dyld) 之后可以運用這些位置動態(tài)查找,加載,鏈接。
動態(tài)鏈接:
當app啟動時,內核新建一個進程,在新的進程中加載可執(zhí)行文件,即你寫的程序, 和dynamic linker(dyld),內核在動態(tài)鏈接器中執(zhí)行程序,而動態(tài)鏈接器則會加載程序中引用的庫。
具體地說,動態(tài)鏈接器會依次執(zhí)行以下一些工作:
- 在內核分配的進程的最原始的棧里面啟動自己
- 遞歸地加載所有的程序中導入的動態(tài)庫到進程的內存空間,動態(tài)鏈接器會緩存這些加載過程
- 把這些加載到進程的庫鏈接到可執(zhí)行文件,這一過程中會立即綁定那些需要立即綁定的symbol,并且為那些不需要立即綁定的symbol建立一個表
- 為可執(zhí)行文件運行靜態(tài)初始化函數,對于 Objective-C 的類而言,就是
+load方法,對于 C++ 的類而言就是構造函數 - 準備可執(zhí)行文件的
main函數的參數并且調用main函數 - 在進程執(zhí)行的過程中, 當可執(zhí)行文件需要調用之前沒有綁定的symbol時,
dyld會通過之前生成的表綁定這些symbol,dyld也通過dl開頭的一些 API 的功能提供運行時加載的服務,提供對gdb以及其他 debugger 的掛鉤來獲得有用的信息 - 當
main結束返回之后,運行靜態(tài)終止函數,比如 C++ 中的析構函數 - 在一些場景里,當
main函數返回之后會調用 libSystem's_exit