如何讓static library中的category變得可用【譯】

版權(quán)聲明:本文源自簡書【九昍】晚树,歡迎轉(zhuǎn)載,轉(zhuǎn)載請務(wù)必注明出處: http://www.reibang.com/p/cb5e73adb6c3

編譯器把源文件(.c受葛,.cc题涨,.cpp偎谁,.m)轉(zhuǎn)化成對象文件(.o),源文件和對象文件是一一對應(yīng)的纲堵。對象文件(object file)里包含了符號巡雨、代碼和數(shù)據(jù),這種文件并不是直接被操作系統(tǒng)使用的席函。

當(dāng)你組建(build)動態(tài)庫(.dylib)铐望、framework、bundle(bundle)或者一個可執(zhí)行的二進(jìn)制文件的時候茂附,這些文件被鏈接器鏈接到一起來生成一些操作系統(tǒng)認(rèn)為“可用的”的東西正蛙,例如一些可以直接載入指定內(nèi)存地址的東西。

當(dāng)開發(fā)者組建(build)靜態(tài)庫(static library)的時候营曼,所有的對象文件被簡單的添加到一個大的歸檔文件里乒验,這就是.a(archive)文件的由來。所以.a文件就是一些對象文件的歸檔(想象一下沒有經(jīng)過壓縮的tar歸檔或者zip歸檔)蒂阱《腿拷貝單個.a文件要比拷貝一堆.o文件簡單的多(java也是一樣,為了使用方便你可以把一些.class文件放到一個.jar歸檔里)录煤。

在把二進(jìn)制鏈接到靜態(tài)庫(=archive)的時候鳄厌,編譯器會獲得一張含有歸檔里所有符號的表,然后編譯器會檢查哪些符號被這些二進(jìn)制文件引用了妈踊。只有包含被引用的符號的對象文件會被鏈接器真正的載入了嚎,并被鏈接進(jìn)程處理。例如廊营,如果你的文檔中有50個對象文件歪泳,但是只有20個包含被二進(jìn)制使用的符號,那么只有這20個文件會被鏈接器載入赘风,另外30個會被鏈接進(jìn)程完全忽略夹囚。

在C和C++代碼里,這種機(jī)制能很好地工作邀窃,因?yàn)檫@些語言會盡可能的在編譯期(C++也有一些runtime特性)去做這些事荸哟。然而Objective-C是一種與眾不同的語言,OC非常依賴runtime特性瞬捕,而且很多OC的特性都是只支持runtime的鞍历。OC類里面實(shí)際上也有類似于C函數(shù)或全局C變量的符號表(至少現(xiàn)在的OC runtime是這樣)。編譯器可以識別出一個類有沒有被引用肪虎,從而確定這個類是不是被使用了劣砍。如果你使用了靜態(tài)庫里的對象文件中的類,這個對象文件就會被鏈接器載入扇救,因?yàn)殒溄悠靼l(fā)現(xiàn)它的一個符號被使用了刑枝。

Category是runtime下特有的特性香嗓,它并不會像類或者函數(shù)一樣被符號化,也就是說装畅,編譯器并不能檢測到Category是不是被使用了靠娱。
如果鏈接器載入了一個包含OC代碼的對象文件,這些OC代碼的所有部分都是編譯階段的一部分掠兄。所以當(dāng)一個包含Category的對象文件因?yàn)槿魏畏柋徽J(rèn)為“在使用”(可能是一個類像云,可能是一個函數(shù),也可能是一個全局變量)蚂夕,它的Category也會被載入并在運(yùn)行期變得可用迅诬。基于前面的描述婿牍,一個只包含Cagegory的對象文件里沒有被編譯器認(rèn)為“在使用”的符號侈贷,所以是不會被載入的。

為了使在靜態(tài)庫中的類別能被使用牍汹,可以通過下面的5中方法解決:

  1. 通過在Other Linker Flags添加-all_load铐维,它會告訴編譯器“對于所有文檔中的所有對象文件柬泽,不管里面的符號有沒有被用到慎菲,全部都載入”,這種方法確實(shí)可以锨并,但是會產(chǎn)生比較大的二進(jìn)制文件露该。

  2. 另一種方法是添加-force_load和指定的路徑,這種方法和-all_load很像第煮,不同的是它只載入指定的歸檔解幼。

  3. 最受歡迎的方法是在Other Linker Flags中添加-ObjC,這個標(biāo)識告訴編譯器“如果你在文檔里的對象文件中發(fā)現(xiàn)了OC代碼包警,就把它載入“撵摆,Category里當(dāng)然也有OC代碼。使用這種方法不會載入任何沒有OC代碼的文件

  4. 另一種解決方法是新Xcode里build setting中的 Perform Single-Object PreLink害晦,如果啟用這個選項(xiàng)特铝,所有的對象文件都會被合并成一個單文件(這不是真正的鏈接,所以叫做預(yù)鏈接)壹瘟,這個對象文件(有時被稱做主對象文件(master object file))被添加到文檔中■杲耍現(xiàn)在如果主對象文件中的任何符號被認(rèn)為是“在使用”,整個主對象文件都會被認(rèn)為在使用稻轨,這樣它里面的OC部分就會被載入了灵莲。因?yàn)槔锩娴念惗急徽7柣耍阅苁箯倪@樣的靜態(tài)庫中使用所有的Category殴俱。

  5. 最后一種解決方法是在只有Category的源文件里添加Fake symbol政冻。如果你想在運(yùn)行時使用Category枚抵,一定要確保你以某種方法在編譯時引用了fake symbol,這會使得對象文件以及它里面的OC代碼被載入明场。例如俄精,它可以是一個有空函數(shù)體的函數(shù),也可以是一個被訪問的全局變量(例如一個全局的int變量榕堰,只要它被讀或者寫了一次就足夠了)竖慧。和上面其他的解決方法不一樣,這種解決方法可以控制哪些category可以在運(yùn)行時被編譯后的代碼使用(可以通過使用這個符號逆屡,使它們被鏈接并變得可用圾旨;也可以不使用這個符號,這樣鏈接器就會忽略它)魏蔗。

如果你有一個包含上百個對象的靜態(tài)庫砍的,但是你的二進(jìn)制文件只使用了其中的幾個,應(yīng)該避免使用1~4的方法莺治。否則會得到一個非常大的包含了所有類(可能這些類根本沒被用到)的二進(jìn)制文件廓鞠。對于一個Class,通常不用做特殊的處理谣旁;而對于一個Category床佳,考慮一下e中解決方案,它可以讓你只包含想要的Category榄审。

例如:如果想使用NSData的類別砌们,添加兩個方法compressionData和decompressionData,可以創(chuàng)建一個這樣的頭文件搁进。

// NSData+Compress.h
@interface NSData (Compression)
    - (NSData *)compressedData;
    - (NSData *)decompressedData;
@end

void import_NSData_Compression ( ); //注意這個方法*************

再添加一個這樣的實(shí)現(xiàn)文件

// NSData+Compress
@implementation NSData (Compression)
    - (NSData *)compressedData 
    {
        // ... magic ...
    }
 
    - (NSData *)decompressedData
    {
        // ... magic ...
    }
@end
 
void import_NSData_Compression ( ) { } //注意這里***************

然后確保在代碼里調(diào)用了import_NSData_Compression這個方法浪感。是不是真的調(diào)用或者調(diào)用的次數(shù)并不重要,實(shí)際上并不需要真的去調(diào)用它饼问,只需要讓編譯器認(rèn)為你調(diào)用了這個方法就行了影兽,比如可以把這段代碼添加到工程的任何地方

__attribute__((used)) static void importCategories ()
{
   import_NSData_Compression();
   // add more import calls here
}

這樣寫以后并不需要在代碼里調(diào)用importCategories (),attribute將會讓編譯器認(rèn)為它被調(diào)用了莱革。

如果添加了-whyload標(biāo)識峻堰,鏈接器會打印組件日志告訴你“哪個二進(jìn)制文件里的哪個對象文件因?yàn)槟囊粋€符號被使用所以被載入了”。但是它只會打印第一個被認(rèn)為是“使用中”的符號驮吱。

-dead strip對OC不適用盔粹,不感興趣的可以不往下看了

編譯器還有一個屬性叫做 –dead_strip幻赚,如果編譯器決定再入一個對象文件,這個文件里的所有符號都會變成鏈接后的二進(jìn)制文件中的一部分,不管它們有沒有被使用衅斩。比如有一個對象文件包含了100個函數(shù)船老,但是它們中只有一個被二進(jìn)制文件使用了,所有的100個函數(shù)仍然會被添加到二進(jìn)制文件里,因?yàn)閷ο笪募荒鼙煌暾奶砑踊蛘卟槐惶砑诱纭f溄悠鞑恢С植糠痔砑訉ο笪募?/p>

但是,如果告訴鏈接器“dead strip”牧氮, 鏈接器首先會把所有的對象文件添加到二進(jìn)制文件琼腔,解決所有的引用,然后掃描二進(jìn)制文件中的符號是不是在被使用(或者是被一些沒有被使用的符號使用)踱葛。所有的被找到的沒有被使用的符號都會在優(yōu)化階段被移除丹莲。在上面的例子里,99個沒有被使用的函數(shù)會被移除尸诽。如果你使用了-load_all,-force_load或者Perform Single-Object PreLink甥材,這個標(biāo)識會非常有用,因?yàn)檫@三個選項(xiàng)在一些情況下很容易增加二進(jìn)制文件的大小性含,而dead strip將會移除那些沒用的代碼和數(shù)據(jù)洲赵。
Dead strip對于C代碼能很好的工作(例如:像預(yù)期的那樣去掉沒用的函數(shù)、變量和常量)商蕴,它在C++上也能工作的不錯(例如:沒用的類能夠被移除)叠萍。雖然它并不完美,在一些情況下一些符號沒有被移除绪商,但是在大多數(shù)情況下它能在這些語言下很好地工作苛谷。

在OC
OC里面,并不支持dead strp部宿,因?yàn)镺O是一種具有runtime特性的語言抄腔,編譯器并不能在編譯時確定符號是不是真的被使用了。例如理张,如果沒有代碼直接使用一個OC的類,那他就是沒有被使用嗎绵患?當(dāng)然不是雾叭,開發(fā)者可以動態(tài)創(chuàng)建一個包含類名的字符串,獲得這個類的一個指針落蝙,并動態(tài)的執(zhí)行alloc织狐。
比如:

MyCoolClass * mcc = [[MyCoolClass alloc] init];

也可以寫成

NSString * cname = @"CoolClass";
NSString * cnameFull = [NSString stringWithFormat:@"My%@", cname];
Class mmcClass = NSClassFromString(cnameFull);
id mmc = [[mmcClass alloc] init];

第二種寫法并沒有直接引用這個類,對象的創(chuàng)建通過runtime完成筏勒。

原文鏈接http://stackoverflow.com/questions/2567498/objective-c-categories-in-static-library
感謝作者 Mecki

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末移迫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子管行,更是在濱河造成了極大的恐慌厨埋,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捐顷,死亡現(xiàn)場離奇詭異荡陷,居然都是意外死亡雨效,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門废赞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來徽龟,“玉大人,你說我怎么就攤上這事唉地【莼冢” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵耘沼,是天一觀的道長屠尊。 經(jīng)常有香客問我,道長耕拷,這世上最難降的妖魔是什么讼昆? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮骚烧,結(jié)果婚禮上浸赫,老公的妹妹穿的比我還像新娘。我一直安慰自己赃绊,他們只是感情好既峡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碧查,像睡著了一般运敢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上忠售,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天传惠,我揣著相機(jī)與錄音,去河邊找鬼稻扬。 笑死卦方,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泰佳。 我是一名探鬼主播盼砍,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逝她!你這毒婦竟也來了浇坐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤黔宛,失蹤者是張志新(化名)和其女友劉穎近刘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡跌宛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年酗宋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疆拘。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜕猫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出哎迄,到底是詐尸還是另有隱情回右,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布漱挚,位于F島的核電站翔烁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏旨涝。R本人自食惡果不足惜蹬屹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望白华。 院中可真熱鬧慨默,春花似錦、人聲如沸弧腥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽管搪。三九已至虾攻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間更鲁,已是汗流浹背霎箍。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岁经,地道東北人朋沮。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像缀壤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子纠亚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

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