iOS符號(hào)沖突(duplicate symbols)

前言

背景是我們項(xiàng)目升級(jí)某個(gè)SDK评姨,結(jié)果發(fā)現(xiàn)項(xiàng)目和SDK出現(xiàn)符號(hào)沖突贪磺。
符號(hào)沖突是接入SDK有可能會(huì)出現(xiàn)的問(wèn)題赋朦,本文便嘗試從技術(shù)角度去解決。

正文

因?yàn)楸旧眄?xiàng)目本身不便公開(kāi)芦岂,所以新建兩個(gè)工程來(lái)模擬這個(gè)場(chǎng)景瘪弓。(工程代碼地址)
LYTestFramework靜態(tài)庫(kù)工程,里面帶有不公開(kāi)的SSUser類(lèi)禽最,模擬SDK腺怯;
LearnSymbol普通工程,模擬項(xiàng)目的主工程川无,里面也有SSUser類(lèi)呛占;

將LYTestFramework手動(dòng)導(dǎo)入LearnSymbol工程:

這樣便出現(xiàn)了兩個(gè)SSUser:
一個(gè)是LYTestFramework.framework內(nèi)不公開(kāi)的SSUser;

@implementation SSUser
// from framework's user
- (void)test {
    NSLog(@"framework test");
}
@end

另一個(gè)是LearnSymbol工程內(nèi)自己帶的SSUser懦趋;

@implementation SSUser
// from project's user
- (void)test {
    NSLog(@"main test");
}
@end

那么編譯的時(shí)候晾虑,就會(huì)出現(xiàn)duplicate symbol _OBJC_CLASS_$_SSUser的錯(cuò)誤。

可是仅叫,當(dāng)我真正開(kāi)始運(yùn)行的時(shí)候帜篇,才發(fā)現(xiàn)竟然編譯通過(guò)了:

對(duì)比了這個(gè)新建工程和原工程的Other Linker Flags,發(fā)現(xiàn)是因?yàn)樾鹿こ躺倭艘粋€(gè)-ObjC的設(shè)置诫咱,另外原工程還有-l secXXX的flag笙隙。

回顧下-ObjC 、 -all_load 坎缭、-force_load這三個(gè)flag的區(qū)別:

  • -ObjC 鏈接器會(huì)加載靜態(tài)庫(kù)中所有的Objective-C類(lèi)和Category竟痰;(導(dǎo)致可執(zhí)行文件變大)
  • -all_load 鏈接器會(huì)加載靜態(tài)庫(kù)中所有的Objective-C類(lèi)和Category(這里和上面一樣)签钩;當(dāng)靜態(tài)庫(kù)只有Category時(shí)-ObjC會(huì)失效,需要使用這個(gè)flag坏快;
  • -force_load 加載特定靜態(tài)庫(kù)的全部類(lèi)铅檩,與-all_load類(lèi)似但是只限定于特定靜態(tài)庫(kù),所以-force_load需要指定靜態(tài)庫(kù)莽鸿;當(dāng)兩個(gè)靜態(tài)庫(kù)存在同樣的符號(hào)時(shí)柠并,使用-all_load會(huì)出現(xiàn)duplicate symbol的錯(cuò)誤,此時(shí)可以選擇將其中一個(gè)庫(kù)-force_load富拗;(需要注意兩個(gè)庫(kù)的版本是不是一致的)

所以這里的直接編譯通過(guò)的原因:工程中已經(jīng)有了SSUser類(lèi)的符號(hào)臼予,所以鏈接的時(shí)候會(huì)直接使用工程中的SSUser符號(hào),所以編譯運(yùn)行完的結(jié)果是調(diào)用了工程中的SSUser類(lèi)啃沪,靜態(tài)庫(kù)中的SSUser并沒(méi)有被鏈接粘拾。

而原工程的-l secXXX的鏈接flag是什么意思?
gcc有三個(gè)很像的參數(shù)创千,分別是-l -l -L缰雇,第一個(gè)I是i的大寫(xiě)桃笙,中間的是L的小寫(xiě)l。

  • -I怜森,用于指定頭文件的地址殿雪;
  • -l暇咆,用于指定具體的靜態(tài)庫(kù)、動(dòng)態(tài)庫(kù)丙曙;
  • -L爸业,用于指定庫(kù)文件的地址;

回到我們的工程亏镰,我們往Other Linker Flags添加-ObjC的flag之后扯旷,再次嘗試編譯。

此時(shí)終于復(fù)現(xiàn)了之前的符號(hào)沖突:

duplicate symbol _OBJC_CLASS_$_SSUser in:
    /Users/loyinglin/Library/Developer/Xcode/DerivedData/LearnSymbol-dhlwaeprifzeedegywrvodujmcoj/Build/Intermediates.noindex/LearnSymbol.build/Debug-iphonesimulator/LearnSymbol.build/Objects-normal/x86_64/SSUser.o
    /Users/loyinglin/Documents/Learn/LearnDuplicateSymbol/LearnSymbol/LearnSymbol/LYTestFramework.framework/LYTestFramework(SSUser.o)
duplicate symbol _OBJC_METACLASS_$_SSUser in:
    /Users/loyinglin/Library/Developer/Xcode/DerivedData/LearnSymbol-dhlwaeprifzeedegywrvodujmcoj/Build/Intermediates.noindex/LearnSymbol.build/Debug-iphonesimulator/LearnSymbol.build/Objects-normal/x86_64/SSUser.o
    /Users/loyinglin/Documents/Learn/LearnDuplicateSymbol/LearnSymbol/LearnSymbol/LYTestFramework.framework/LYTestFramework(SSUser.o)
ld: 2 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

接下來(lái)從嘗試技術(shù)的角度去解決這個(gè)問(wèn)題:
解決方案1:去掉主工程的SSUser索抓,用靜態(tài)庫(kù)里面的SSUser钧忽;

不可行,靜態(tài)庫(kù)的SSUser沒(méi)有開(kāi)放頭文件逼肯,無(wú)法使用耸黑;

解決方案2:在主工程的compile source去掉SSUser.m文件,工程中僅用SSUser.h文件來(lái)調(diào)用汉矿;(假設(shè)兩方用的是同個(gè)版本)
嘗試編譯崎坊,符號(hào)沖突可以解決备禀;
運(yùn)行的結(jié)果表示調(diào)用了LYTestFramework中的SSUser:

2019-07-14 14:13:21.767218+0800 LearnSymbol[28982:5102302] framework test

解決方案3:去掉LYTestFramework靜態(tài)庫(kù)中的SSUser符號(hào)洲拇,鏈接時(shí)全部使用主工程的SSUser奈揍;
我們知道靜態(tài)庫(kù)是多個(gè).o文件組成的,那么我們可以找到SSUser.o然后剔除赋续,靜態(tài)庫(kù)依賴(lài)的SSUser會(huì)在鏈接時(shí)找到主工程生成的SSUser.o男翰;
我們先進(jìn)入打包的出來(lái)的LYTestFramework.framework文件夾,目錄如下:

我們?cè)贖eaders的同級(jí)目錄創(chuàng)建一個(gè)目錄pack纽乱,將LYTestFramework這個(gè)文件移動(dòng)到pack目錄中蛾绎。
ar -t LYTestFramework指令,可以看到這個(gè)庫(kù)中的.o文件包括SSUser.o鸦列,下面嘗試手動(dòng)移除這個(gè)SSUser.o文件:

  • 1租冠、先將LYTestFramework解壓:ar xv LYTestFramework
  • 2薯嗤、手動(dòng)刪除SSUser.o文件顽爹;
  • 3、回到上級(jí)目錄骆姐,重新把.o文件打包:ar rcs LYTestFramework pack/*.o镜粤;

再用ar -t LYTestFramework指令查看,發(fā)現(xiàn)SSUser.o已經(jīng)不見(jiàn)玻褪,重新打包成功運(yùn)行肉渴,結(jié)果表示調(diào)用了主工程的SSUser:

2019-07-17 16:20:33.576468+0800 LearnSymbol[86290:7683465] main test

附1:這為了簡(jiǎn)化邏輯,這里只有模擬器的cpu架構(gòu)带射,沒(méi)有包括armv7/arm64同规,用 lipo -info LYTestFramework指令可以看到:
LYTestFramework is architecture: x86_64;
如果有多種cpu架構(gòu)窟社,需要分別對(duì)每種架構(gòu)進(jìn)行處理捻浦,再合并。

附2:以上的解決方案均是假設(shè)兩方用的是同個(gè)靜態(tài)庫(kù)版本桥爽。如果是不同版本朱灿,則需要修改命名,使得多個(gè)版本的靜態(tài)庫(kù)可以共存钠四。

另一個(gè)Linking中的選項(xiàng):

Dead Code Stripping 是對(duì)程序編譯出的可執(zhí)行二進(jìn)制文件中沒(méi)有被實(shí)際使用的代碼進(jìn)行Strip操作盗扒。
Dead code stripping removes code that the compiler determines is unreachable.
代碼舉例:

總結(jié)

符號(hào)沖突是引入第三方庫(kù)的時(shí)候,有可能會(huì)遇到的問(wèn)題缀去。
當(dāng)庫(kù)A和庫(kù)B的符號(hào)出現(xiàn)沖突時(shí)侣灶,如果庫(kù)A和庫(kù)B沖突的符號(hào),是功能相同的符號(hào)缕碎,則可以選擇去掉其中一個(gè)符號(hào)褥影,選擇只加載其中一個(gè)庫(kù)的符號(hào)。
如果兩個(gè)符號(hào)所表示的意義不同咏雌,比如說(shuō)不來(lái)自同一個(gè)庫(kù)(僅僅是命名一樣凡怎,導(dǎo)致符號(hào)沖突)校焦,或者來(lái)自同一個(gè)庫(kù)但是版本不同,這種只能通過(guò)重命名或者修改庫(kù)的代碼邏輯來(lái)實(shí)現(xiàn)共存统倒。

附錄

靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)的思考
編譯與鏈接過(guò)程的思考
https://blog.csdn.net/djl4104804/article/details/43099061
https://garbageout.wordpress.com/2015/03/24/dead-code-stripping/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寨典,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子房匆,更是在濱河造成了極大的恐慌耸成,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浴鸿,死亡現(xiàn)場(chǎng)離奇詭異井氢,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)岳链,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)毙沾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人宠页,你說(shuō)我怎么就攤上這事左胞。” “怎么了举户?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵烤宙,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我俭嘁,道長(zhǎng)躺枕,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任供填,我火速辦了婚禮拐云,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘近她。我一直安慰自己叉瘩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布粘捎。 她就那樣靜靜地躺著薇缅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪攒磨。 梳的紋絲不亂的頭發(fā)上泳桦,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音娩缰,去河邊找鬼灸撰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浮毯。 我是一名探鬼主播完疫,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼亲轨!你這毒婦竟也來(lái)了趋惨?” 一聲冷哼從身側(cè)響起鸟顺,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤惦蚊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后讯嫂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蹦锋,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年欧芽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了莉掂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡千扔,死狀恐怖憎妙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情曲楚,我是刑警寧澤厘唾,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站龙誊,受9級(jí)特大地震影響抚垃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜趟大,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一鹤树、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逊朽,春花似錦罕伯、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至绽榛,卻和暖如春湿酸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背灭美。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工推溃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人届腐。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓铁坎,卻偏偏與公主長(zhǎng)得像蜂奸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子硬萍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容