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

Objective-C是一門動(dòng)態(tài)語言,它將很多靜態(tài)語言在編譯和鏈接時(shí)期做的事放到了運(yùn)行時(shí)來處理。這樣處理也就意味著,它將使我們的代碼更有靈活性。比如,我們可以根據(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ī)器說話的編譯器,還要有讓代碼隨心而動(dòng)的運(yùn)行時(shí),而這個(gè)運(yùn)行時(shí)就是objc Runtime。它基本上是由C和匯編實(shí)現(xiàn)的,它讓C具有了面向?qū)ο蟮哪芰?/strong>。

Runtime庫主要做下面幾件事:

  • 封裝:在這個(gè)庫中,對(duì)象可以用C語言中的結(jié)構(gòu)體表示,而方法可以用C函數(shù)來實(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ì)象的能力,為什么這么說呢,我們可以看一下下面的這個(gè)例子:

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

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

  • 打開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;
}

  • 打開命令行,編譯main.m為.cpp文件,打開文件可以看到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的初始化過程為例,簡(jiǎn)要分析一下其實(shí)現(xiàn)過程:
首先,在原文件中的

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

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

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類型來表示的,它實(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)無限延伸下去,Objective-C的設(shè)計(jì)者讓所有的meta-class的isa指向基類的meta-class,以此作為它們的所屬類。即,任何NSObject繼承體系下的meta-class都使用NSObject的meta-class作為自己的所屬類,而基類的meta-class的isa指針是指向它自己。這樣就形成了一個(gè)完美的閉環(huán)。
通過上面的描述,再加上對(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í),需要問自己的村長(zhǎng)有沒有,并在有的情況下調(diào)用,如果沒有呢?當(dāng)你要去干掉一條龍,意氣風(fēng)發(fā)的去跟村長(zhǎng)要?jiǎng)r(shí),發(fā)現(xiàn)他竟然沒有,當(dāng)時(shí)你就要崩潰,村長(zhǎng)一看,“哎喲喲,你可別死我家門口,我問問我老爹有沒有”,然后就給該隱打電話,向他借,再不行就讓該隱向亞當(dāng)借,這要是都沒有。。以諾就會(huì)告訴村民“行了,大家一起死吧,你要的東西大家都沒有”。然后全世界就崩潰掉了(野指針:指向了未識(shí)別的方法)。
這時(shí)候你就要問了,那subclass(meta)又是何方神圣呢?好吧,我也不知道她叫啥,是以諾的媳婦就是了,她藏著以諾的小金庫,里邊裝著村長(zhǎng)能用的兵器(類方法),當(dāng)村長(zhǎng)要用時(shí)就得向她申請(qǐng),同理,她沒有就得向她媽要,在這里因?yàn)榕訜o需上戰(zhàn)場(chǎng),所以她們本身要兵器。。你猜她們想干嘛,所以她們想要兵器直接向RootClass(meta)夏娃說句悄悄話, 夏娃想要兵器也沒地要去,看看自己有啥吧。秉承著男傳男,女傳女的原則,可以很清楚的看清圖中的繼承關(guān)系。等等,天吶,RootClass(meta)繼承于RootClass,這是咋回事?嘿嘿,別忘了,夏娃是用亞當(dāng)?shù)囊桓吖窃斐鰜淼?。而亞?dāng),你能跟造出他那位打通電話嗎?


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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評(píng)論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,312評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,993評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,410評(píng)論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,778評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,955評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,521評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,266評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,468評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,696評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評(píng)論 1 294
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,193評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,431評(píng)論 2 378

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