Objective-C Runtimeの類與對(duì)象

Objective-C是一門(mén)動(dòng)態(tài)語(yǔ)言紧显,它將很多靜態(tài)語(yǔ)言在編譯和鏈接時(shí)期做的事放到了運(yùn)行時(shí)來(lái)處理撇他。這樣處理也就意味著逛裤,它將使我們的代碼更有靈活性指孤。比如启涯,我們可以根據(jù)我們的意向?qū)⑾⑥D(zhuǎn)發(fā)給其它對(duì)象,或者去替換我們想要實(shí)現(xiàn)的方法等恃轩。因?yàn)镺bjective-C“動(dòng)態(tài)化”的內(nèi)容都是在運(yùn)行時(shí)完成的结洼,所以,OC的運(yùn)行條件不僅僅要求有幫助我們向機(jī)器說(shuō)話的編譯器叉跛,還要有讓代碼隨心而動(dòng)的運(yùn)行時(shí)松忍,而這個(gè)運(yùn)行時(shí)就是objc Runtime。它基本上是由C和匯編實(shí)現(xiàn)的筷厘,它讓C具有了面向?qū)ο蟮哪芰?/strong>。

Runtime庫(kù)主要做下面幾件事:

  • 封裝:在這個(gè)庫(kù)中酥艳,對(duì)象可以用C語(yǔ)言中的結(jié)構(gòu)體表示摊溶,而方法可以用C函數(shù)來(lái)實(shí)現(xiàn),另外再加上了一些額外的特性玖雁。這些結(jié)構(gòu)體和函數(shù)被runtime函數(shù)封裝后更扁,我們就可以在程序運(yùn)行時(shí)創(chuàng)建,檢查,修改類浓镜、對(duì)象和它們的方法了溃列。
  • 找出方法的最終執(zhí)行代碼:當(dāng)程序執(zhí)行[object doSomething]時(shí),會(huì)向消息接收者(object)發(fā)送一條消息(doSomething)膛薛,runtime會(huì)根據(jù)消息接收者是否能響應(yīng)該消息而做出不同的反應(yīng)听隐。

類與對(duì)象

Objective-C是對(duì)C的進(jìn)一步封裝,讓C有了面對(duì)對(duì)象的能力哄啄,為什么這么說(shuō)呢雅任,我們可以看一下下面的這個(gè)例子:

Person *per = [[Person alloc] init];

這是一個(gè)很簡(jiǎn)單的獲取實(shí)例化對(duì)象的方法。
那么這個(gè)語(yǔ)句在經(jīng)過(guò)編譯后會(huì)變成什么樣呢咨跌,想看的話我們可以這樣做:

  • 打開(kāi)Xcode沪么,創(chuàng)建一個(gè)命令行文件

  • 創(chuàng)建一個(gè).m 文件锌半,在里邊定義我們想要的類禽车, 內(nèi)容如下所示:

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, assign) NSInteger age;

@end

@implementation Person

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        p.age = 18;
        NSLog(@"%ld", p.age);
    }
    return 0;
}

  • 打開(kāi)命令行,編譯main.m為.cpp文件刊殉,打開(kāi)文件可以看到main函數(shù)中的實(shí)現(xiàn)如下:
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ 
    { 
        __AtAutoreleasePool __autoreleasepool; 
        Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
        ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)p, sel_registerName("setAge:"), (NSInteger)18);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_dd_c58dp2w556l44jqlhxdgthdh0000gn_T_main_449954_mi_0, ((NSInteger (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("age")));
    }
    return 0;
}

在以上函數(shù)中殉摔,采用了消息發(fā)送機(jī)制,可以看到记焊,OC在這里也脫去了它面向?qū)ο蟮耐庖乱菰拢@露除了些許本質(zhì)。
以實(shí)例化對(duì)象p的初始化過(guò)程為例遍膜,簡(jiǎn)要分析一下其實(shí)現(xiàn)過(guò)程:
首先碗硬,在原文件中的

Person *p = [[Person alloc]init];

先后調(diào)用了兩個(gè)方法,也即發(fā)送了兩個(gè)消息瓢颅, 對(duì)應(yīng)的肛响,編譯后文件就是實(shí)現(xiàn)這一過(guò)程。

Person *(*)(id, SEL)(void *)objc_msgSend((id)objc_getClass("Person"), sel_registerName("alloc"));

這一方法對(duì)應(yīng)的就是Person調(diào)用的alloc方法惜索,objc_msgSend函數(shù)發(fā)送alloc消息(需要強(qiáng)轉(zhuǎn)),它將會(huì)返回一個(gè)結(jié)構(gòu)體(Person的實(shí)例化對(duì)象)剃浇。同理巾兆,返回的對(duì)象在執(zhí)行初始化方法init時(shí),需要再次發(fā)送消息虎囚,也就有了在.cpp文件中我們看到的樣子:

Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));

*注:在.m中如果也想如此實(shí)現(xiàn)角塑,需要引用<objc/runtime.h>和<objc/message.h>


數(shù)據(jù)結(jié)構(gòu)

Class

Objective-C類是由Class類型來(lái)表示的,它實(shí)際上是一個(gè)指向objc_class結(jié)構(gòu)體的指針淘讥。
查看objc/runtime.h中objc_class結(jié)構(gòu)體的定義如下:

struct objc_class {

    Class isa  OBJC_ISA_AVAILABILITY;



#if !__OBJC2__

    Class super_class                       OBJC2_UNAVAILABLE;  // 父類

    const char *name                        OBJC2_UNAVAILABLE;  // 類名

    long version                            OBJC2_UNAVAILABLE;  // 類的版本信息圃伶,默認(rèn)為0

    long info                               OBJC2_UNAVAILABLE;  // 類信息,供運(yùn)行期使用的一些位標(biāo)識(shí)

    long instance_size                      OBJC2_UNAVAILABLE;  // 該類的實(shí)例變量大小

    struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  // 該類的成員變量鏈表

    struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定義的鏈表

    struct objc_cache *cache                OBJC2_UNAVAILABLE;  // 方法緩存

    struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 協(xié)議鏈表

#endif



} OBJC2_UNAVAILABLE;

MetaClass

在objc_class的結(jié)構(gòu)體中有isa這么一個(gè)字段,在Objective-C中窒朋,所有的類自身也是一個(gè)對(duì)象搀罢,這個(gè)對(duì)象的Class里面也有一個(gè)isa指針,它指向metaClass(元類)侥猩。
當(dāng)我們向一個(gè)對(duì)象發(fā)送消息時(shí)榔至,runtime會(huì)在這個(gè)對(duì)象所屬的這個(gè)類的方法列表中查找方法;而向一個(gè)類發(fā)送消息時(shí)欺劳,會(huì)在這個(gè)類的meta-class的方法列表中查找唧取。
meta-class之所以重要,是因?yàn)樗鎯?chǔ)著一個(gè)類的所有類方法划提。每個(gè)類都會(huì)有一個(gè)單獨(dú)的meta-class枫弟,因?yàn)槊總€(gè)類的類方法基本不可能完全相同。
再深入一下鹏往,meta-class也是一個(gè)類淡诗,也可以向它發(fā)送一個(gè)消息,那么它的isa又是指向什么呢掸犬?為了不讓這種結(jié)構(gòu)無(wú)限延伸下去袜漩,Objective-C的設(shè)計(jì)者讓所有的meta-class的isa指向基類的meta-class,以此作為它們的所屬類湾碎。即宙攻,任何NSObject繼承體系下的meta-class都使用NSObject的meta-class作為自己的所屬類,而基類的meta-class的isa指針是指向它自己介褥。這樣就形成了一個(gè)完美的閉環(huán)座掘。
通過(guò)上面的描述,再加上對(duì)objc_class結(jié)構(gòu)體中super_class指針的分析柔滔,我們就可以描繪出類及相應(yīng)meta-class類的一個(gè)繼承體系了溢陪,如下圖所示:

繼承關(guān)系Class與Meta

我是這樣去理解這個(gè)圖的:
圖中的各個(gè)類的關(guān)系以及isa指向就如同一個(gè)老亞家的家譜以及財(cái)產(chǎn)關(guān)系,首先圖中的subClass可以看做是以諾睛廊, 他管理著一個(gè)村落中的所有兵器(instance of Class的方法)形真,當(dāng)村民需要使用時(shí),需要問(wèn)自己的村長(zhǎng)有沒(méi)有超全,并在有的情況下調(diào)用咆霜,如果沒(méi)有呢?當(dāng)你要去干掉一條龍嘶朱,意氣風(fēng)發(fā)的去跟村長(zhǎng)要?jiǎng)r(shí)蛾坯,發(fā)現(xiàn)他竟然沒(méi)有,當(dāng)時(shí)你就要崩潰疏遏,村長(zhǎng)一看脉课,“哎喲喲救军,你可別死我家門(mén)口,我問(wèn)問(wèn)我老爹有沒(méi)有”倘零,然后就給該隱打電話唱遭,向他借,再不行就讓該隱向亞當(dāng)借视事,這要是都沒(méi)有胆萧。。以諾就會(huì)告訴村民“行了俐东,大家一起死吧跌穗,你要的東西大家都沒(méi)有”。然后全世界就崩潰掉了(野指針:指向了未識(shí)別的方法)虏辫。
這時(shí)候你就要問(wèn)了蚌吸,那subclass(meta)又是何方神圣呢?好吧砌庄,我也不知道她叫啥羹唠,是以諾的媳婦就是了,她藏著以諾的小金庫(kù)娄昆,里邊裝著村長(zhǎng)能用的兵器(類方法)佩微,當(dāng)村長(zhǎng)要用時(shí)就得向她申請(qǐng),同理萌焰,她沒(méi)有就得向她媽要哺眯,在這里因?yàn)榕訜o(wú)需上戰(zhàn)場(chǎng),所以她們本身要兵器扒俯。奶卓。你猜她們想干嘛,所以她們想要兵器直接向RootClass(meta)夏娃說(shuō)句悄悄話撼玄, 夏娃想要兵器也沒(méi)地要去夺姑,看看自己有啥吧。秉承著男傳男掌猛,女傳女的原則盏浙,可以很清楚的看清圖中的繼承關(guān)系。等等荔茬,天吶只盹,RootClass(meta)繼承于RootClass,這是咋回事兔院?嘿嘿,別忘了站削,夏娃是用亞當(dāng)?shù)囊桓吖窃斐鰜?lái)的坊萝。而亞當(dāng),你能跟造出他那位打通電話嗎?


類與對(duì)象的操作函數(shù)

見(jiàn)Objective-C Runtime 運(yùn)行時(shí)之一:類與對(duì)象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末十偶,一起剝皮案震驚了整個(gè)濱河市菩鲜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惦积,老刑警劉巖接校,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異狮崩,居然都是意外死亡蛛勉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)睦柴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诽凌,“玉大人,你說(shuō)我怎么就攤上這事坦敌÷滤校” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵狱窘,是天一觀的道長(zhǎng)杜顺。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蘸炸,這世上最難降的妖魔是什么躬络? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮幻馁,結(jié)果婚禮上洗鸵,老公的妹妹穿的比我還像新娘。我一直安慰自己仗嗦,他們只是感情好膘滨,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著稀拐,像睡著了一般火邓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上德撬,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天铲咨,我揣著相機(jī)與錄音,去河邊找鬼蜓洪。 笑死纤勒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的隆檀。 我是一名探鬼主播摇天,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼粹湃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了泉坐?” 一聲冷哼從身側(cè)響起为鳄,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腕让,沒(méi)想到半個(gè)月后孤钦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纯丸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年偏形,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片液南。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡壳猜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出滑凉,到底是詐尸還是另有隱情统扳,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布畅姊,位于F島的核電站咒钟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏若未。R本人自食惡果不足惜朱嘴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粗合。 院中可真熱鬧萍嬉,春花似錦、人聲如沸隙疚。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)供屉。三九已至行冰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伶丐,已是汗流浹背悼做。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哗魂,地道東北人肛走。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像录别,于是被迫代替她去往敵國(guó)和親羹与。 傳聞我的和親對(duì)象是個(gè)殘疾皇子故硅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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