符號表和編譯鏈
一個程序從簡單易讀的代碼到可執(zhí)行文件往往要經(jīng)歷以下步驟:
源代碼 > 預(yù)處理器 > 編譯器 > 匯編器 > 機器碼 > 鏈接器 > 可執(zhí)行文件
源文件經(jīng)過一系列處理以后烤镐,會生成對應(yīng)的.o文件,然后一個項目必然會有許多.o文件市俊,并且這些文件之間會有各種各樣的聯(lián)系,例如函數(shù)調(diào)用。鏈接器做的事就是把這些目標(biāo)文件和所用的一些庫鏈接在一起形成一個完整的可執(zhí)行文件仅政。
通俗的講兴想,我們在源碼中寫的全局變量名
斩例、函數(shù)名
或類名
在生成的*.o對象文件中都叫做符號吼具,存在一個叫做符號表的地方僚纷。
舉個例子:
我們在a.c文件中寫了一個函數(shù)叫foo()矩距,然后在main.c文件中調(diào)用了foo()函數(shù)拗盒,在將源碼編譯生成的對象文件中a.o對象文件中的符號表里保存著foo()函數(shù)符號,并通過該符號可以定位到a.o文件中關(guān)于foo()方法的具體實現(xiàn)代碼锥债。
鏈接器在鏈接生成最終的二進制程序的時候會發(fā)現(xiàn)main.o對象文件中引用了符號foo()陡蝇,而foo()符號并沒有在main.o文件中定義,所以不會存在與main.o對象文件的符號表中哮肚,于是鏈接器就開始檢查其他對象文件登夫,當(dāng)檢查到a.o文件中定義了符號foo(),于是就將a.o對象文件鏈接進來允趟。這樣就確保了在main.c中能夠正常調(diào)用a.c中實現(xiàn)的foo()方法了恼策。
注意: Objective-C 中方法的執(zhí)行是在運行時決定的,所以鏈接器只鏈接類的符號潮剪,不鏈接方法的符號
.a靜態(tài)庫
一般可能是.a靜態(tài)庫和工程里面的某個類重復(fù)了涣楷,得到錯誤比如:
duplicate symbol _OBJC_CLASS_$_GTMBase64 in:
*****
duplicate symbol _OBJC_METACLASS_$_GTMBase64 in:
*****
ld: 2 duplicate symbols for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
從上面錯誤我們可以看出在arm64
這個架構(gòu)的靜態(tài)鏈接庫出現(xiàn)了類名重復(fù),我們可以得到對應(yīng)架構(gòu)的靜態(tài)鏈接庫里面所有類和分類的信息(以.o結(jié)尾的文件)抗碰,所以可以把某個重復(fù)的類刪除了狮斗,重新打包成靜態(tài)庫。具體參考處理.a靜態(tài)庫弧蝇。
Other Linker Flags
一般你的工程里Other Linker Flags
會設(shè)置了-ObjC
碳褒。如果我們刪除了-ObjC
折砸,就會發(fā)現(xiàn)在編譯時不會有duplicate symbols
的錯誤了。但是運行的時候可能會出現(xiàn)unrecognized selector sent to class 0x105494cd8
的錯誤沙峻。這是由于靜態(tài)庫中的分類
沒被鏈接器鏈接進可執(zhí)行文件中睦授。
對于ObjC
,官方解釋如下:
This flag causes the linker to load every object file in the library that defines an Objective-C class or category. While this option will typically result in a larger executable (due to additional object code loaded into the application), it will allow the successful creation of effective Objective-C static libraries that contain categories on existing classes.
簡單的說就是ObjC
會把靜態(tài)庫中所有的類和分類都鏈接進可執(zhí)行文件摔寨,但是它不會去檢查此類是否重復(fù)了睹逃,所以才會出現(xiàn)duplicate symbols
的錯誤。
所以這里我們想要的就是只把分類給鏈接進去祷肯,而ObjC
顯然會帶出其他問題沉填。
這里有另一個配置-force_load
,這個的作用其實跟ObjC
是類似的佑笋。它是只指定某一個靜態(tài)庫才執(zhí)行鏈接所有類和分類的操作翼闹,而ObjC
表示的是所以靜態(tài)庫。
所以理想的狀態(tài)就是不使用ObjC
蒋纬,只有包含分類的靜態(tài)庫才使用-force_load
猎荠,這樣既能避免一些不必要的文件使包變大,又能使出現(xiàn)duplicate symbols
的概率減小蜀备。
為什么只是減小呢关摇?因為如果你這個靜態(tài)庫里即有重復(fù)文件又存在分類
,那只能使用-force_load
碾阁,同時那個重復(fù)的類也鏈接進來了输虱,這樣duplicate symbols
就還會出現(xiàn)。
總結(jié)解決辦法
- 如果是工程里的類跟靜態(tài)庫重名了脂凶,那可以重命名工程里面的類名宪睹。
- 如果是兩個靜態(tài)庫有重名了,可以解壓靜態(tài)庫蚕钦,把重復(fù)的類刪了亭病,然后重新打包并合并兩個靜態(tài)庫(要確保兩個重復(fù)類結(jié)構(gòu)相同)
- 如果重復(fù)的類所在的靜態(tài)庫沒有分類,那
Other Linker Flags
中可以去掉ObjC
嘶居,只在有分類的靜態(tài)庫前加上-force_load
罪帖。
參考
ios 靜態(tài)庫沖突的解決辦法
解決兩個靜態(tài)庫沖突 duplicate symbols
How can I avoid “duplicate symbol” errors in xcode with shared static libraries?