靜態(tài)庫中的類別符號(-ObjC\-all_load\-force_load)

1. 項目結(jié)構(gòu):

image.png

其中,SimpleStatic是一個靜態(tài)庫項目,我們將頭文件Person.hPerson+MyPerson.h暴露出來供外部使用.

Symbol工程是主項目.

main.m中的代碼為:

#import <Foundation/Foundation.h>
#import <Person.h>
#import <Person+MyPerson.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
      
        [p todoSomething];
        
        //extern void test(void);
        
        //test();
    }
    return 0;
}

Person.m中代碼為:

#import "Person.h"
@implementation Person
- (void)aabbccdd {
    
}

Person+MyPerson.m中代碼為:

#import "Person+MyPerson.h"

void test() {
    NSLog(@"這是test");
}

@implementation Person (MyPerson)
- (void)todoSomething {
    NSLog(@"這是person分類");
}
@end

Config.xcconfig中內(nèi)容為:

LD_MAP_FILE_PATH = ${SRCROOT}/myfile.m
LD_GENERATE_MAP_FILE = YES

這主要是為了生成link map文件.

2. 運行

此時,build項目Symbol會發(fā)現(xiàn)沒有任何問題.
然而,運行時發(fā)現(xiàn)項目崩潰:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person todoSomething]: unrecognized selector sent to instance 0x1004814f0'

根據(jù)網(wǎng)上資料,可以很容易的查到,這是因為main.o編譯成可執(zhí)行文件的時候,沒有鏈接Person+MyPerson.o導(dǎo)致的.解決方法是在Other linker flags中添加-ObjC.它的含義是:鏈接靜態(tài)庫中所有包含OC類和類別的目標(biāo)文件

3. 如何驗證

可以通過兩次生成的link map文件進(jìn)行查看.
打開myfile.m:

# Object files:
    [  0] linker synthesized
    [  1] /Users/LQ/Library/Developer/Xcode/DerivedData/Symbol-akbspkizvxjwrmamodsanattllyd/Build/Intermediates.noindex/Symbol.build/Debug/Symbol.build/Objects-normal/x86_64/main.o
    [  2] /Users/LQ/Library/Developer/Xcode/DerivedData/Symbol-akbspkizvxjwrmamodsanattllyd/Build/Products/Debug/SimpleStatic(Person.o)
    ...

由此可以判斷出鏈接了哪些目標(biāo)文件.

現(xiàn)在,把main.m中調(diào)用test()方法的地方打開注釋,重新運行.

注意:此時沒有添加-ObjC.

結(jié)果發(fā)現(xiàn)link map文件中已經(jīng)可以鏈接到Person+MyPerson.o

...
[  2] /Users/LQ/Library/Developer/Xcode/DerivedData/Symbol-akbspkizvxjwrmamodsanattllyd/Build/Products/Debug/SimpleStatic(Person.o)
[  3] /Users/LQ/Library/Developer/Xcode/DerivedData/Symbol-akbspkizvxjwrmamodsanattllyd/Build/Products/Debug/SimpleStatic(Person+MyPerson.o)

4. 為什么明明調(diào)用了分類的方法todoSomething,但是沒有鏈接到分類?而假如在分類.m中定義一個c函數(shù)void test(void) {},在項目中調(diào)用test()卻能鏈接到SimpleStatic(Person+MyPerson.o)?

這是因為C和OC有所區(qū)別:

當(dāng)源文件使用在另一個文件中定義的東西(比如函數(shù))時侣集,一個未定義的符號被寫入到目標(biāo)文件中飞蚓,以“替代”丟失的東西厦幅。鏈接器通過在構(gòu)建最終可執(zhí)行文件時拉入包含未定義符號定義的對象文件來解析這些符號偷霉。

例如,如果main.c使用函數(shù)foo()甚垦,其中foo在另一個文件B.c中定義蜜托,那么對象文件main.o將具有foo()的未解析符號科侈,而B.o將包含foo()的實現(xiàn)。在鏈接時辅辩,B.o將被引入到最終的可執(zhí)行文件中难礼,因此main.o中的代碼現(xiàn)在引用B.o中定義的foo()的實現(xiàn)。

UNIX靜態(tài)庫只是對象文件的集合玫锋。通常蛾茉,如果這樣做會解析一些未定義的符號,那么鏈接器僅從靜態(tài)庫中提取對象文件撩鹿。不拉入所有對象文件會減小最終可執(zhí)行文件的大小臀稚。

Objective-C的動態(tài)特性使事情稍微復(fù)雜一些。因為實現(xiàn)方法的代碼直到方法被實際調(diào)用才確定三痰,所以O(shè)bjective-C不為方法定義鏈接器符號吧寺。鏈接器符號僅為類定義。

例如散劫,如果main.m包含代碼[[FooClass alloc]initWithBar:nil]稚机;那么main.o將包含F(xiàn)ooClass的未定義符號,但是-initWithBar:方法的鏈接器符號將不在main.o中获搏。

由于類別是方法的集合赖条,因此使用類別的方法不會生成未定義的符號。這意味著鏈接器不知道加載定義類別的對象文件(如果類本身已經(jīng)定義)常熙。這會導(dǎo)致和未實現(xiàn)方法時一樣的運行時錯誤"selector not recognized" 纬乍。

參考鏈接:oc靜態(tài)庫和類別

根據(jù)以上說法實現(xiàn)方法的代碼直到方法被實際調(diào)用才確定,猜測是因為方法調(diào)用本質(zhì)上是objc_msgSend,因此不會生成方法名的符號.

而調(diào)用C函數(shù)test()是因為需要鏈接Person+MyPerson.o,此時會一并將此目標(biāo)文件中的其他符號導(dǎo)入,因此不會發(fā)生崩潰.

5.如何驗證猜想?

首先將main.m編譯成目標(biāo)文件:

clang -fmodules -c main.m -o main.o -I/Users/LQ/Desktop/Test/OC/Symbol/SimpleStatic/SimpleStatic

注意:此時注釋掉main中調(diào)用test函數(shù)的地方,并且沒有添加-ObjC

使用nm命令查看該文件的符號表如下:

LQ-Pro:Symbol LQ$ nm ./main.o
                 U _NSLog
0000000000000078 s _OBJC_CLASSLIST_REFERENCES_$_
                 U _OBJC_CLASS_$_Person
00000000000000b8 s _OBJC_SELECTOR_REFERENCES_
                 U ___CFConstantStringClassReference
0000000000000000 T _main
                 U _objc_alloc_init
                 U _objc_autoreleasePoolPop
                 U _objc_autoreleasePoolPush
                 U _objc_msgSend

可以看到,此時main.o中根本沒有OC方法todoSomething的鏈接符號,取而代之的則是_objc_msgSend.

現(xiàn)在,打開test()函數(shù)的注釋,重新查看main.o中的符號:

0000000000000060 s _OBJC_CLASSLIST_REFERENCES_$_
                 U _OBJC_CLASS_$_Person
0000000000000078 s _OBJC_SELECTOR_REFERENCES_
0000000000000000 T _main
                 U _objc_alloc_init
                 U _objc_autoreleasePoolPop
                 U _objc_autoreleasePoolPush
                 U _objc_msgSend
                 U _test

_test符號是未定義的,因此在鏈接階段,就會導(dǎo)入Person+MyPerson.o中的符號.

使用nm查看最終的可執(zhí)行文件:

0000000100003e90 t -[Person aabbccdd]
0000000100003ec0 t -[Person(MyPerson) todoSomething]
                 U _NSLog
                 U _OBJC_CLASS_$_NSObject
0000000100008128 S _OBJC_CLASS_$_Person
                 U _OBJC_METACLASS_$_NSObject
0000000100008100 S _OBJC_METACLASS_$_Person
0000000100008028 s __OBJC_$_INSTANCE_METHODS_Person(MyPerson)
00000001000080a8 s __OBJC_CLASS_RO_$_Person
0000000100008060 s __OBJC_METACLASS_RO_$_Person
                 U ___CFConstantStringClassReference
0000000100008150 d __dyld_private
0000000100000000 T __mh_execute_header
                 U __objc_empty_cache
0000000100003e00 T _main
                 U _objc_alloc_init
                 U _objc_autoreleasePoolPop
                 U _objc_autoreleasePoolPush
                 U _objc_msgSend
                 U _objc_storeStrong
0000000100003ea0 T _test
                 U dyld_stub_binder

可以看到分類和類的符號都導(dǎo)入了.

6. 其他

  1. 對于使用OC方法的文件(即main.m引用Person的方法),不會生成該方法的鏈接符號;但是對于定義OC方法的文件(即Person.m),會生成鏈接符號.

前半句意思是在main.o中,是不能看到OC的方法符號的.

后半句的意思是,假設(shè)我們此時去查看Person.oPerson+MyPerson.o,是可以分別看到-[Person aabbccdd]-[Person(MyPerson) todoSomething]符號的.

  1. Other linker flags一些選項
  • -ObjC:強(qiáng)制靜態(tài)庫中所有實現(xiàn)了OC類和類別的.o文件被鏈接
  • -all_load:強(qiáng)制加載所有靜態(tài)庫中的目標(biāo)文件.這是因為當(dāng)靜態(tài)庫中只有OC類別時,-ObjC還是無法鏈接類別
  • -fore_load:后面必須跟一個靜態(tài)庫路徑.強(qiáng)制加載單個靜態(tài)庫所有目標(biāo)文件.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市裸卫,隨后出現(xiàn)的幾起案子仿贬,更是在濱河造成了極大的恐慌,老刑警劉巖墓贿,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茧泪,死亡現(xiàn)場離奇詭異,居然都是意外死亡聋袋,警方通過查閱死者的電腦和手機(jī)队伟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來幽勒,“玉大人嗜侮,你說我怎么就攤上這事。” “怎么了锈颗?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵缠借,是天一觀的道長。 經(jīng)常有香客問我宜猜,道長泼返,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任姨拥,我火速辦了婚禮绅喉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叫乌。我一直安慰自己柴罐,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布憨奸。 她就那樣靜靜地躺著革屠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪排宰。 梳的紋絲不亂的頭發(fā)上似芝,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天,我揣著相機(jī)與錄音板甘,去河邊找鬼党瓮。 笑死,一個胖子當(dāng)著我的面吹牛盐类,可吹牛的內(nèi)容都是我干的寞奸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼在跳,長吁一口氣:“原來是場噩夢啊……” “哼枪萄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起猫妙,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤瓷翻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吐咳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逻悠,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年韭脊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片单旁。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡沪羔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蔫饰,我是刑警寧澤琅豆,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站篓吁,受9級特大地震影響茫因,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜杖剪,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一冻押、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盛嘿,春花似錦洛巢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芥炭,卻和暖如春漓库,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背园蝠。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工米苹, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人砰琢。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓蘸嘶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親陪汽。 傳聞我的和親對象是個殘疾皇子训唱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,554評論 2 349

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