iOS 程序引入framework 類別報(bào)錯(cuò)unrecognized selector sent to class

背景

在ios開發(fā)過程中,有時(shí)候會(huì)用到第三方的靜態(tài)庫(.a文件),然后導(dǎo)入后發(fā)現(xiàn)編譯正常但運(yùn)行時(shí)會(huì)出現(xiàn)selector not recognized的錯(cuò)誤,從而導(dǎo)致app閃退。接著仔細(xì)閱讀庫文件的說明文檔,你可能會(huì)在文檔中發(fā)現(xiàn)諸如在Other Linker Flags中加入-ObjC或者-all_load這樣的解決方法。

那么,Other Linker Flags到底是用來干什么的呢?還有-ObjC和-all_load到底發(fā)揮了什么作用呢?

鏈接器

首先,要說明一下Other Linker Flags到底是用來干嘛的。說白了,就是ld命令除了默認(rèn)參數(shù)外的其他參數(shù)。ld命令實(shí)現(xiàn)的是鏈接器的工作,詳細(xì)說明可以在終端man ld查看。

如果有人不清楚鏈接器是什么東西的話,我可以作個(gè)簡(jiǎn)單的說明。

一個(gè)程序從簡(jiǎn)單易讀的代碼到可執(zhí)行文件往往要經(jīng)歷以下步驟:

源代碼 > 預(yù)處理器 > 編譯器 > 匯編器 > 機(jī)器碼 > 鏈接器 > 可執(zhí)行文件

源文件經(jīng)過一系列處理以后,會(huì)生成對(duì)應(yīng)的.obj文件,然后一個(gè)項(xiàng)目必然會(huì)有許多.obj文件,并且這些文件之間會(huì)有各種各樣的聯(lián)系,例如函數(shù)調(diào)用。鏈接器做的事就是把這些目標(biāo)文件和所用的一些庫鏈接在一起形成一個(gè)完整的可執(zhí)行文件。

可能我描述的比較膚淺,因?yàn)槲易约毫私獾囊膊皇呛苌?,建議大家讀一下這篇文章,可以對(duì)鏈接器做的事情有個(gè)大概的了解:鏈接器做了什么

為什么會(huì)閃退

蘋果官方Q&A上有這么一段話:

The "selector not recognized" runtime exception occurs due to an issue between the implementation of standard UNIX static libraries, the linker and the dynamic nature of Objective-C. Objective-C does not define linker symbols for each function (or method, in Objective-C) - instead, linker symbols are only generated for each class. If you extend a pre-existing class with categories, the linker does not know to associate the object code of the core class implementation and the category implementation. This prevents objects created in the resulting application from responding to a selector that is defined in the category.

翻譯過來,大概意思就是Objective-C的鏈接器并不會(huì)為每個(gè)方法建立符號(hào)表,而是僅僅為類建立了符號(hào)表。這樣的話,如果靜態(tài)庫中定義了已存在的一個(gè)類的分類,鏈接器就會(huì)以為這個(gè)類已經(jīng)存在,不會(huì)把分類和核心類的代碼合起來。這樣的話,在最后的可執(zhí)行文件中,就會(huì)缺少分類里的代碼,這樣函數(shù)調(diào)用就失敗了。

解決方法

解決方法在背景那塊我就提到了,就是在Other Linker Flags里加上所需的參數(shù),用到的參數(shù)一般有以下3個(gè):

-ObjC

-all_load

-force_load

下面來說說每個(gè)參數(shù)存在的意義和具體做的事情。

首先是-ObjC,一般這個(gè)參數(shù)足夠解決前面提到的問題,蘋果官方說明如下:

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.

簡(jiǎn)單說來,加了這個(gè)參數(shù)后,鏈接器就會(huì)把靜態(tài)庫中所有的Objective-C類和分類都加載到最后的可執(zhí)行文件中,雖然這樣可能會(huì)因?yàn)榧虞d了很多不必要的文件而導(dǎo)致可執(zhí)行文件變大,但是這個(gè)參數(shù)很好地解決了我們所遇到的問題。但是事實(shí)真的是這樣的嗎?

如果-ObjC參數(shù)真的這么有效,那么事情就會(huì)簡(jiǎn)單多了。

Important: For 64-bit and iPhone OS applications, there is a linker bug that prevents -ObjC from loading objects files from static libraries that contain only categories and no classes. The workaround is to use the -allload or -forceload flags.

當(dāng)靜態(tài)庫中只有分類而沒有類的時(shí)候,-ObjC參數(shù)就會(huì)失效了。這時(shí)候,就需要使用-all_load或者-force_load了。

-all_load會(huì)讓鏈接器把所有找到的目標(biāo)文件都加載到可執(zhí)行文件中,但是千萬不要隨便使用這個(gè)參數(shù)!假如你使用了不止一個(gè)靜態(tài)庫文件,然后又使用了這個(gè)參數(shù),那么你很有可能會(huì)遇到ld: duplicate symbol錯(cuò)誤,因?yàn)椴煌膸煳募锩婵赡軙?huì)有相同的目標(biāo)文件,所以建議在遇到-ObjC失效的情況下使用-force_load參數(shù)。

-force_load所做的事情跟-all_load其實(shí)是一樣的,但是-force_load需要指定要進(jìn)行全部加載的庫文件的路徑,這樣的話,你就只是完全加載了一個(gè)庫文件,不影響其余庫文件的按需加載。

最后附上一個(gè)蘋果官方對(duì)framework中使用類別,并報(bào)selector not recognized 的解釋,有興趣的可以看一看:

Building Objective-C static libraries with categories

本文為轉(zhuǎn)載

原文鏈接:http://blog.csdn.net/meegomeego/article/details/19343423

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

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

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