加入-ObjC連接參數後項目走火入魔的事件由來以久,往日只曾據說,卻從未見過。最近門派開發產品的時候,本人經身經歷了一番。html
公司項目遇到的狀況以下:首先咱們公司的項目是用CocoaPod構建的,CocoaPod在生成項目的時候會自動在Other Link Flags配置項上打上-ObjC,並且,即便強行去除-ObjC選項也沒法解決,雖然編譯可經過,可是運行的時候,友盟、MJRefresh等衆多庫均會報錯沒法運行。
同時,公司的項目基於另外一個分公司所提供的一個基礎服務framework,這個framework具體實現未知,目前看來應該是使用c++開發,同時庫必須禁用掉-ObjC選項,不然會報 duplicated symbols錯誤,編譯都沒法經過。
因而坑就這樣出來了。c++
簡單來講,-ObjC連接指令是用來解決static library在運行時調用category方法報selector not recognized錯誤時使用的。也就是說,若是你在一個static library裏面聲明瞭一個category,在運行的時候調用這個方法就頗有可能會出現這個錯誤,而這個錯誤本不該該出現,由於你已經定義了那個方法。
那麼,爲何會出現這樣一個問題?objective-c
簡單來講,這是由於UNIX 的靜態庫(.a文件)與OC的動態機制之間的不協調致使的。
咱們先來看通常狀況下,UNIX靜態庫及C程序的一個連接過程。當一個C語言程序編譯的時候,全部的源代碼會被編譯爲對象文件,即.o文件(object file)。這些對象文件中包含了相應的可執行程序,以及相應的靜態數據。連接器最終須要將全部這些對象文件組合到一塊兒從而產生一個最終的可執行文件。
當一個源文件引用了定義於其餘文件中的一些東西的時候(好比引用其餘文件的一個方法),一個 undefined symbol就被寫入了它所產生的object 文件,而後等待最終被解釋掉。在最終構建可執行文件的時候,連接器將從包含這些undefined symbols的object文件中拉取信息以解決掉以前被標記的undefined symbols。
一個UNIX的靜態庫其實就是一系統object file的集合,然而,通常狀況下只會拉取那些,他須要的object file。而這樣作的好處是能夠減少最終可執行文件的大小。app
好比main.c使用一個函數,名叫foo( ),而這個函數定義於B.c裏面。在生成.o文件的時候,main.o就會有一個foo( ) 的undefined symbols標記。在連接期間,B.o文件便會被打入最終的可執行文件中。可是,假如另外還有一個C.c,裏面定義了的函數並沒被使用到,那麼最終,這個C.o文件便不會出如今最終的可執行文件中。函數
然而衆所周之,oc是具備必定動態性的語言,只有在最終運行的時候,對象方法的具體實現只有到被調用的時候纔會被肯定。基於這個緣由,Objective-C並無對方法級別定義符號,而是隻對類級別定義符號。
舉個栗子。好比在main.c中包含如下代碼: [[FooClass alloc] initWithBar:nil];
那麼在連接的時候,main.o在生成的時候就會包含一個未定義符號(Undefined Symbols) FooClass,但卻不會定義符號 initWithBar。code
坑爹的是,Category只是一個方法的集合,而對一個Cateogry中的方法的調用,並不會生成未定義符號,這就意味着連接器並不知道要去加載這個Cateogry文件所生成的object 文件。因而,在最後運行的時候,運行時系統便會沒法找到相應方法的定義,從而拋出unrecognized selector 錯誤。htm
那爲啥加上-ObjC就行了? 原來,加上-ObjC選項的時候,連接器便會加載靜態Library裏面全部Objective-C實現的類和Cateogry。對象
除了-ObjC,文檔上還有幾個其餘的選項,也值得咱們關注一下。事件
-all_load 全加載,意思是加載全部靜態庫的成員。不管是c仍是c++還oc。開發
-force_load path_to_load 對某個指定靜態庫全加載。而-all_load則會對全部的庫進行全加載。
這到這裏,這個問題貌似已經能夠解決了。嗯,分公司的開發確定在framework裏面打包了幾個沒有用到的額外同名實現,而加上-ObjC,會將全部方法全加載上,因而duplicated symbols錯誤便被拋出。
那麼,若是libPod,也就是pod庫生成的靜態庫前面加上-force_load,而不加上-ObjC,問題不就順利解決了?
參考文章: <<Apple Technical Q&A QA1490 >>
<<Objective-C categories in static library>>
<<Linker error using CocoaPods>>