項(xiàng)目是用CocoaPod構(gòu)建的,CocoaPod在生成項(xiàng)目的時(shí)候會(huì)自動(dòng)在Other Link Flags配置項(xiàng)上打上-ObjC茁肠,而且季惩,即使強(qiáng)行去除-ObjC選項(xiàng)也無法解決,雖然編譯可通過掘剪,但是運(yùn)行的時(shí)候平委,友盟、MJRefresh等眾多庫均會(huì)報(bào)錯(cuò)無法運(yùn)行夺谁。
同時(shí)廉赔,公司的項(xiàng)目基于另一個(gè)分公司所提供的一個(gè)基礎(chǔ)服務(wù)framework,這個(gè)framework具體實(shí)現(xiàn)未知匾鸥,目前看來應(yīng)該是使用c++開發(fā)蜡塌,同時(shí)庫必須禁用掉-ObjC選項(xiàng),否則會(huì)報(bào) duplicated symbols錯(cuò)誤勿负,編譯都無法通過馏艾。
于是坑就這樣出來了。
-ObjC是干嘛的奴愉?
簡單來說琅摩,-ObjC鏈接指令是用來解決static library在運(yùn)行時(shí)調(diào)用category方法報(bào)selector not recognized錯(cuò)誤時(shí)使用的。也就是說锭硼,如果你在一個(gè)static library里面聲明了一個(gè)category房资,在運(yùn)行的時(shí)候調(diào)用這個(gè)方法就很有可能會(huì)出現(xiàn)這個(gè)錯(cuò)誤,而這個(gè)錯(cuò)誤本不應(yīng)該出現(xiàn)檀头,因?yàn)槟阋呀?jīng)定義了那個(gè)方法轰异。那么,為什么會(huì)出現(xiàn)這樣一個(gè)問題鳖擒?
為啥會(huì)出 method not recognized 錯(cuò)誤
簡單來說溉浙,這是因?yàn)閁NIX 的靜態(tài)庫(.a文件)與OC的動(dòng)態(tài)機(jī)制之間的不協(xié)調(diào)導(dǎo)致的。
我們先來看一般情況下蒋荚,UNIX靜態(tài)庫及C程序的一個(gè)鏈接過程戳稽。當(dāng)一個(gè)C語言程序編譯的時(shí)候,所有的源代碼會(huì)被編譯為對象文件期升,即.o文件(object file)惊奇。這些對象文件中包含了相應(yīng)的可執(zhí)行程序,以及相應(yīng)的靜態(tài)數(shù)據(jù)播赁。鏈接器最終需要將所有這些對象文件組合到一起從而產(chǎn)生一個(gè)最終的可執(zhí)行文件颂郎。
當(dāng)一個(gè)源文件引用了定義于其他文件中的一些東西的時(shí)候(比如引用其他文件的一個(gè)方法),一個(gè) undefined symbol就被寫入了它所產(chǎn)生的object 文件容为,然后等待最終被解釋掉乓序。在最終構(gòu)建可執(zhí)行文件的時(shí)候寺酪,鏈接器將從包含這些undefined symbols的object文件中拉取信息以解決掉之前被標(biāo)記的undefined symbols。
一個(gè)UNIX的靜態(tài)庫其實(shí)就是一系統(tǒng)object file的集合替劈,然而寄雀,一般情況下只會(huì)拉取那些,他需要的object file陨献。而這樣做的好處是可以減小最終可執(zhí)行文件的大小盒犹。
舉個(gè)例子
比如main.c使用一個(gè)函數(shù),名叫foo( )眨业,而這個(gè)函數(shù)定義于B.c里面急膀。在生成.o文件的時(shí)候,main.o就會(huì)有一個(gè)foo( ) 的undefined symbols標(biāo)記龄捡。在鏈接期間卓嫂,B.o文件便會(huì)被打入最終的可執(zhí)行文件中。但是墅茉,假如另外還有一個(gè)C.c命黔,里面定義了的函數(shù)并沒被使用到,那么最終就斤,這個(gè)C.o文件便不會(huì)出現(xiàn)在最終的可執(zhí)行文件中悍募。
Objective-C有什么不同?
然而眾所周之洋机,oc是具有一定動(dòng)態(tài)性的語言坠宴,只有在最終運(yùn)行的時(shí)候,對象方法的具體實(shí)現(xiàn)只有到被調(diào)用的時(shí)候才會(huì)被確定绷旗∠补模基于這個(gè)原因,Objective-C并沒有對方法級(jí)別定義符號(hào)衔肢,而是只對類級(jí)別定義符號(hào)庄岖。
舉個(gè)栗子。比如在main.c中包含以下代碼: [[FooClass alloc] initWithBar:nil]; 那么在鏈接的時(shí)候角骤,main.o在生成的時(shí)候就會(huì)包含一個(gè)未定義符號(hào)(Undefined Symbols) FooClass隅忿,但卻不會(huì)定義符號(hào) initWithBar。
所以為什么會(huì)產(chǎn)生這個(gè)坑邦尊?
坑爹的是背桐,Category只是一個(gè)方法的集合,而對一個(gè)Cateogry中的方法的調(diào)用蝉揍,并不會(huì)生成未定義符號(hào)链峭,這就意味著鏈接器并不知道要去加載這個(gè)Cateogry文件所生成的object 文件。于是又沾,在最后運(yùn)行的時(shí)候弊仪,運(yùn)行時(shí)系統(tǒng)便會(huì)無法找到相應(yīng)方法的定義熙卡,從而拋出unrecognized selector 錯(cuò)誤。
-ObjC干了啥励饵?
那為啥加上-ObjC就好了再膳? 原來,加上-ObjC選項(xiàng)的時(shí)候曲横,鏈接器便會(huì)加載靜態(tài)Library里面所有Objective-C實(shí)現(xiàn)的類和Cateogry。
還有啥選項(xiàng)不瓶?
除了-ObjC禾嫉,文檔上還有幾個(gè)其他的選項(xiàng),也值得我們關(guān)注一下蚊丐。
-all_load 全加載熙参,意思是加載所有靜態(tài)庫的成員。無論是c還是c++還oc麦备。
-force_load path_to_load 對某個(gè)指定靜態(tài)庫全加載孽椰。而-all_load則會(huì)對所有的庫進(jìn)行全加載。
這到這里凛篙,這個(gè)問題貌似已經(jīng)可以解決了黍匾。嗯,分公司的開發(fā)肯定在framework里面打包了幾個(gè)沒有用到的額外同名實(shí)現(xiàn)呛梆,而加上-ObjC锐涯,會(huì)將所有方法全加載上,于是duplicated symbols錯(cuò)誤便被拋出填物。那么纹腌,如果libPod,也就是pod庫生成的靜態(tài)庫前面加上-force_load滞磺,而不加上-ObjC升薯,問題不就順利解決了?
例如:
編譯用TARGET_BUILD_DIR击困,打包可用BUILT_PRODUCTS_DIR
-force_load $(TARGET_BUILD_DIR)/MJRefresh/libMJRefresh.a?
-force_load $(SRCROOT)/Pods/UMengUShare/UShareSDK/SocialLibraries/WeChat/WechatSDK/libWeChatSDK.a
framework類型的需要之后加 ?/庫名?
-force_load $(SRCROOT)/Pods/UMengUShare/UShareSDK/SocialLibraries/QQ/QQSDK/TencentOpenAPI.framework/TencentOpenAPI
引用:https://segmentfault.com/a/1190000005859469
參考:http://www.cocoachina.com/ios/20170110/18549.html涎劈,