Xcode鏈接中遇到的坑

前些日子項(xiàng)目組發(fā)現(xiàn)一個(gè)問題,百度地圖庫跟其他庫A發(fā)生了沖突。這個(gè)沖突在編譯的時(shí)候并沒有檢查出來,而是在運(yùn)行時(shí)發(fā)現(xiàn)百度地圖顯示出現(xiàn)了異常。具體現(xiàn)象是:如果庫A添加到工程當(dāng)中,百度地圖將無法正常顯示,如果把庫A移出工程,地圖顯示恢復(fù)正常。該問題涉及到Xcode的鏈接過程,本文用于記錄分析找到問題的整個(gè)過程。
接下來開始排查這個(gè)問題,通過對百度地圖庫與庫A進(jìn)行比較,發(fā)現(xiàn)兩個(gè)庫都引用了一個(gè)第三方庫文件:libcrypt.a,這是一個(gè)有關(guān)加解密的庫,百度地圖跟庫A的文件夾中各自引用了libcrypt.a。從現(xiàn)象上看,工程當(dāng)中引入庫A(實(shí)際并沒有調(diào)用其中接口)就會(huì)導(dǎo)致百度地圖出現(xiàn)問題,也就是說庫A作為一個(gè)并沒有使用到的庫就會(huì)影響到百度地圖的運(yùn)行邏輯。從前面發(fā)現(xiàn)兩個(gè)庫都引用了libcrypt.a可以猜測跟這個(gè)庫有關(guān)系,按理說工程有兩個(gè)相同的庫,即存在同名函數(shù),就會(huì)在編譯的時(shí)候報(bào)錯(cuò),但實(shí)際工程通過了編譯,這跟Xcode的編譯參數(shù)有關(guān),即other linker flag(鏈接標(biāo)志)。
常用的鏈接選項(xiàng)有3個(gè):-ObjC -all_load -force_load。
-ObjC:加了這個(gè)參數(shù)后,鏈接器就會(huì)把靜態(tài)庫中所有的Objective-C類和分類都加載到最后的可執(zhí)行文件中。
-all_load:會(huì)讓鏈接器把所有找到的目標(biāo)文件都加載到可執(zhí)行文件中。
-force_load:所做的事情跟-all_load其實(shí)是一樣的,但是-force_load需要指定要進(jìn)行全部加載的庫文件的路徑,這樣的話,你就只是完全加載了一個(gè)庫文件,不影響其余庫文件的按需加載。
工程當(dāng)中目前使用的linker flag的選項(xiàng)是-ObjC,該參數(shù)本來是Xcode用來解決Unix靜態(tài)庫無法加載分類當(dāng)中的方法,跟現(xiàn)在遇到的問題關(guān)系并不大。但如果將參數(shù)改成-all_load選項(xiàng),會(huì)導(dǎo)致編譯報(bào)符號重復(fù)的錯(cuò)誤。這說明-ObjC除了解決分類的問題外,還有一個(gè)特點(diǎn),就是在鏈接的時(shí)候,鏈接只要找到一個(gè)符號的實(shí)現(xiàn)即可,而-all_load能檢查到多個(gè)符號實(shí)現(xiàn),從而報(bào)錯(cuò)。
在使用-ObjC選項(xiàng)時(shí),可以推測出現(xiàn)問題的來源,百度地圖與庫A各自引用了libcrypt.a庫,在鏈接的時(shí)候,百度地圖引用的libcrypt方法鏈接到了庫A文件夾當(dāng)中的libcrypt.a庫。如下圖所示:

image.png

正常情況下百度地圖應(yīng)該引用了其文件夾下的Libcrypt.a庫,而庫A引用了Libcrypt.a'庫,如實(shí)線所示。但由于使用了-ObjC參數(shù),導(dǎo)致在鏈接的時(shí)候百度地圖鏈接到了Libcrypt.a'庫,如虛線所示,而Libcrypt.a'進(jìn)行了修改,導(dǎo)致兩個(gè)庫的實(shí)現(xiàn)不一致,導(dǎo)致百度地圖出現(xiàn)問題。
為了驗(yàn)證這個(gè)問題,可以對Xcode鏈接庫順序進(jìn)行調(diào)整,通過修改Build Phases當(dāng)中的Link Binary With Libraries當(dāng)中的順序來調(diào)整,將百度地圖文件夾的Libcrypt庫移動(dòng)到A當(dāng)中Libcrypt的前面,此庫在鏈接的時(shí)候會(huì)先找到百度地圖下的Libcrypt.a當(dāng)中的方法。通過運(yùn)行發(fā)現(xiàn)百度地圖能正常顯示,雖然這樣能讓百度地圖正常運(yùn)行,但由于庫A也會(huì)鏈接到Libcrypt.a,可能會(huì)使庫A出現(xiàn)問題,如下圖所示:

image.png

上面的問題涉及到代碼編譯鏈接的過程,編譯鏈接可以分成四個(gè)過程:預(yù)編譯,編譯,匯編以及鏈接。

  • 預(yù)編譯主要處理代碼層面進(jìn)行處理,包括對宏進(jìn)行展開,處理?xiàng)l件預(yù)編譯指令等。
  • 編譯主要對代碼的詞法、語法進(jìn)行分析,最終生成匯編代碼。
  • 匯編主要將匯編代碼轉(zhuǎn)化成機(jī)器碼,生成.o文件。
  • 最后是鏈接過程,主要完成地址和空間分配,符號決議以及重定向三個(gè)過程。

在匯編過程中,不同的模塊會(huì)被編譯成不同的.o文件,如上面的Libcrypt.o一樣。在百度地圖與庫A當(dāng)中都調(diào)用了Libcrypt.o當(dāng)中的函數(shù),由于在匯編過程無法確定最后函數(shù)的鏈接地址,需要在最后的鏈接過程來重定向。上述遇到的問題也就是在最后一步鏈接重定向當(dāng)中所遇到的,在使用-ObjC參數(shù)時(shí),重定向過程找到了另外一個(gè)Libcrypt.o庫,導(dǎo)致出錯(cuò),并且在編譯過程中沒有發(fā)出警告信息。
總結(jié):
在工程當(dāng)中使用第三方庫,如果需要對第三庫里的方法進(jìn)行修改,最好將使用的類及方法進(jìn)行重命名,這樣一方面可以防止重命名的編譯錯(cuò)誤,更重要的是可以防止因?yàn)槭褂昧?ObjC鏈接參數(shù)導(dǎo)致其他庫鏈接到了修改過的庫,導(dǎo)致其他庫可能出現(xiàn)問題。除此之外,在添加其他庫前,可以先使用-all_load參數(shù)先查看一下庫中有哪些類跟方法跟工程出現(xiàn)重命名錯(cuò)誤,這樣在后面遇到問題是心里也有個(gè)底。

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

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容