【iOS】—— 編譯鏈接
編譯流程
編譯流程分為四步
- 預(yù)處理(Prepressing)
- 編譯(Compilation)
- 匯編(Assembly)
- 鏈接(Linking)
預(yù)處理(預(yù)編譯Prepressing)
作為編譯的第一步,首先將main.m
文件編譯成了main.i
文件,指令如下:
clang -E main.m -o main.i
預(yù)處理的過程雖然是將main.m文件編譯成了mian.i文件,但是實際上的過程并不是這么簡單的,那么具體的預(yù)編譯情況是什么樣的呢:
預(yù)編譯是要處理源代碼中以#開頭的所有預(yù)編譯指令。規(guī)則如下:
-
#define
刪除,并展開對應(yīng)的宏定義。 - 處理所有的條件預(yù)編譯指令。如
#if
、#ifdef
、#else
、#endif
。 -
#include
&#import
包含的文件遞歸插入到此處(含#處)。 - 刪除所有的注釋 //、/**/等。
- 添加行號和文件名標(biāo)識。如 # 1 “main.m"(編譯調(diào)試會用到)。
編譯(Compilation)
將main.i
文件編譯成main.s
文件,指令如下:
clang -S main.i -o main.s
這個過程就是把上面的main.i文件進(jìn)行:詞法分析、語法分析、靜態(tài)分析,優(yōu)化生成相應(yīng)的匯編代碼,最終生成main.s
文件。
這里我們需要了解一下這幾個名詞:
- 詞法分析:把源代碼的字符序列分割成一個個
token
(關(guān)鍵字、表示符、字面量、特殊符號),比如把標(biāo)識符放到符號表里面。 - 語法分析: 生成抽象語法樹AST,此時運(yùn)算符號的優(yōu)先級確定了;有些符號具有多重含義也確定了,比如:*是乘號還是對指針取內(nèi)容;
表達(dá)式不合法
、括號不匹配
等等,都會報錯。 - 靜態(tài)分析:分析
類型聲明
和匹配問題
。比如整型和字符串相加,肯定會報錯。 - 中間語法生成:
CodeGen
根據(jù)AST(抽象語法樹)
自上向下逐步翻譯成LLVM IR
,并且對在編譯期就可以確定的表達(dá)式進(jìn)行優(yōu)化,比如代碼里面的a=1+3,可以優(yōu)化成a=4。(假如開啟了bitcode) - 目標(biāo)代碼生成與優(yōu)化: 根據(jù)中間語法生成依賴具體機(jī)器的匯編語言;并優(yōu)化匯編語言。這個過程中,假如有變量且定義在同一個編譯單元里,那么就給這個變量分配空間,確定變量的地址。假如變量或者函數(shù)不定義在這個編譯單元里面,那就等到鏈接的時候才能確定地址。
匯編(Assembly)
將main.s
文件編譯成main.o
文件(也就是我們常說的目標(biāo)文件),指令如下:
clang -c main.s -o main.o
這個過程就是把上面得到的main.s
文件里面的匯編指令翻譯成機(jī)器指令,最終生成等到main.o
鏈接(Linking)
這個過程就是將main.o
編譯成對應(yīng)的Mach-O
文件,也就是我們常說的可執(zhí)行文件,指令如下:
clang main.o -o main
鏈接的本質(zhì)就是把一個或多個目標(biāo)文件和需要的庫(靜態(tài)庫/動態(tài)庫,如果需要的話)組合成一個文件(Mach-O可執(zhí)行文件)
動態(tài)庫和靜態(tài)庫
靜態(tài)庫
之所以稱為靜態(tài)庫,是因為在鏈接階段,會將匯編生成的目標(biāo)文件.o與引用到的庫一起鏈接打包到可執(zhí)行文件中。因此對應(yīng)的鏈接方式稱為靜態(tài)鏈接。
靜態(tài)庫特點:
- 靜態(tài)庫對函數(shù)庫的鏈接是放在編譯時期完成的。
- 程序在運(yùn)行時與函數(shù)庫再無瓜葛,移植方便。
- 浪費空間和資源,因為所有相關(guān)的目標(biāo)文件與牽涉到的函數(shù)庫被鏈接合成一個可執(zhí)行文件。
動態(tài)庫
動態(tài)庫在程序編譯時并不會被連接到目標(biāo)代碼中,而是在程序運(yùn)行是才被載入。不同的應(yīng)用程序如果調(diào)用相同的庫,那么在內(nèi)存里只需要有一份該共享庫的實例,規(guī)避了空間浪費問題。動態(tài)庫在程序運(yùn)行時才被載入,也解決了靜態(tài)庫對程序的更新、部署和發(fā)布頁會帶來麻煩。用戶只需要更新動態(tài)庫即可,增量更新。
動態(tài)庫特點:
- 動態(tài)庫把對一些庫函數(shù)的鏈接載入推遲到程序運(yùn)行的時期。
- 可以實現(xiàn)進(jìn)程之間的資源共享。(因此動態(tài)庫也稱為共享庫)將一些程序升級變得簡單。
- 甚至可以真正做到鏈接載入完全由程序員在程序代碼中控制(顯示調(diào)用)。
靜態(tài)庫和動態(tài)庫的區(qū)別
編譯方式不同:
- 靜態(tài)庫是在編譯時將庫的代碼打包到可執(zhí)行程序中,因此生成的可執(zhí)行程序包含了所有用到的庫函數(shù)的代碼。這樣,當(dāng)程序被調(diào)用時,需要使用哪些庫函數(shù)就直接從可執(zhí)行文件中取出來使用。因為代碼打包進(jìn)了可執(zhí)行程序中,因此靜態(tài)庫的生成通常需要在代碼的編譯階段進(jìn)行。
- 動態(tài)庫則是在運(yùn)行時動態(tài)加載到程序中的,因此生成的可執(zhí)行文件并不包含庫函數(shù)的實現(xiàn)代碼,而只是引用了動態(tài)庫的接口。當(dāng)程序調(diào)用到該庫函數(shù)時,操作系統(tǒng)會將該函數(shù)從動態(tài)庫文件中加載到內(nèi)存中供程序運(yùn)行使用。這樣一來,程序的可執(zhí)行文件會比靜態(tài)庫生成的可執(zhí)行文件小很多。因為代碼加載是在程序運(yùn)行時進(jìn)行的,所以動態(tài)庫的鏈接通常是在程序運(yùn)行之前進(jìn)行。
內(nèi)存使用方式不同:
- 由于靜態(tài)庫的代碼被打包進(jìn)了可執(zhí)行程序中,所以在程序運(yùn)行時,靜態(tài)庫中的代碼被復(fù)制到了程序使用的內(nèi)存中,并一直駐留在內(nèi)存中使用,因此不需要占用額外的內(nèi)存空間。
- 而動態(tài)庫的代碼在程序運(yùn)行時才會被加載到內(nèi)存中,因此動態(tài)庫的代碼實現(xiàn)被復(fù)制進(jìn)內(nèi)存,會占用額外的內(nèi)存空間。但是與靜態(tài)庫相比,動態(tài)庫的內(nèi)存使用方式具有更好的空間和性能優(yōu)勢,因為多個程序可以共享同一個動態(tài)庫,而不需要重復(fù)加載相同的庫文件,從而減少了系統(tǒng)的內(nèi)存占用。
更新和維護(hù)方式不同:
- 靜態(tài)庫的代碼被打包成可執(zhí)行程序的一部分,因此靜態(tài)庫的更新和維護(hù)需要重新進(jìn)行編譯和部署,才能讓所有使用了該靜態(tài)庫的程序都能夠得到更新的代碼。
- 動態(tài)庫可以獨立于程序進(jìn)行更新,因為動態(tài)庫作為一個單獨的文件存在于系統(tǒng)中,可以被多個程序共享。因此,當(dāng)需要更新動態(tài)庫時,只需要替換掉舊的動態(tài)庫文件,不需要重新編譯和部署所有使用了該動態(tài)庫的程序。
因此,如果需要多個程序共享同一個庫,或者需要較少的內(nèi)存占用,則使用動態(tài)庫可能更為合適。如果需要保持部署和更新的穩(wěn)定性,則靜態(tài)庫可能更為適合。
dyld
認(rèn)識 dyld :動態(tài)鏈接器dyld(the dynamic link editor)
是蘋果的動態(tài)鏈接器,是蘋果操作系統(tǒng)一個重要組成部分,在系統(tǒng)內(nèi)核 XNU 完成 Mach-O 文件的加載,做好程序準(zhǔn)備工作之后,交由 dyld
負(fù)責(zé)余下的工作。在 macOS 系統(tǒng)中,dyld 位于 D/usr/lib/dyld
。文章來源:http://www.zghlxwxcb.cn/news/detail-562317.html
dyld2.0和dyld3.0的區(qū)別
dyld 3 包含這三個部分:文章來源地址http://www.zghlxwxcb.cn/news/detail-562317.html
-
進(jìn)程外 Mach-O 分析器和編譯器
(out-of-process mach-o parser)
由于 dyld 2 存在的問題,dyld 3 中將采用提前寫入把結(jié)果數(shù)據(jù)緩存成文件的方式構(gòu)成一個 lauch closure(可以理解為緩存文件) -
進(jìn)程內(nèi)引擎 執(zhí)行 launch closure 處理
(in-process engine)
驗證”lauch closures“是否正確,映射dylib,執(zhí)行main函數(shù)。此時,它不再需要分析mach-o header和執(zhí)行符號查找,節(jié)省了不少時間。 -
launch closure 緩存服務(wù)
(launch closure cache)
系統(tǒng)程序的 lauch closure 直接內(nèi)置在 shared cache 中,而對于第三方APP,將在APP安裝或更新時生成,這樣就能保證 launch closure 總是在 APP 打開之前準(zhǔn)備好。
到了這里,關(guān)于【iOS】—— 編譯鏈接的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!