淺談Objective-C中的類(lèi)與對(duì)象

我們都知道衡奥,Objective-C是一門(mén)基于C語(yǔ)言的面向?qū)ο蟮恼Z(yǔ)言瓤介,它的對(duì)象模型是基于類(lèi)來(lái)建立的坪圾。我們可以在蘋(píng)果開(kāi)源的 runtime中發(fā)現(xiàn) Objective-C 對(duì)象模型的實(shí)現(xiàn)細(xì)節(jié)。

對(duì)象

在 Objective-C 中惑朦,每一個(gè)對(duì)象都是某個(gè)類(lèi)的實(shí)例兽泄,且這個(gè)對(duì)象的 isa指針(在 64 位 CPU 下,isa 已經(jīng)不再是一個(gè)簡(jiǎn)單的指針漾月,在本文中我們暫且把它當(dāng)作普通指針來(lái)理解)指向它所屬的類(lèi)病梢。

類(lèi)

對(duì)象的類(lèi)不僅描述了對(duì)象的數(shù)據(jù):對(duì)象占用的內(nèi)存大小、成員變量的類(lèi)型和布局等梁肿,而且也描述了對(duì)象的行為:對(duì)象能夠響應(yīng)的消息蜓陌、實(shí)現(xiàn)的實(shí)例方法等。因此吩蔑,當(dāng)我們調(diào)用實(shí)例方法 [receiver message] 給一個(gè)對(duì)象發(fā)送消息時(shí)钮热,這個(gè)對(duì)象能否響應(yīng)這個(gè)消息就需要通過(guò) isa 找到它所屬的類(lèi)以及superclass才能知道。

  • id和Class的定義
struct objc_class {
    struct objc_class *isa;
};
struct objc_object {
    struct objc_class *isa;
};
 
typedef struct objc_class *Class; //類(lèi)  (class object)
typedef struct objc_object *id;   //對(duì)象 (instance of class)

在objc中烛芬,id代表了一個(gè)對(duì)象隧期。根據(jù)上面的聲明,凡是首地址是*isa的struct指針赘娄,都可以被認(rèn)為是objc中的對(duì)象仆潮。運(yùn)行時(shí)可以通過(guò)isa指針,查找到該對(duì)象是屬于什么類(lèi)(Class)遣臼。

在 Objective-C 中有一個(gè)非常特殊的類(lèi) NSObject 性置,絕大部分的類(lèi)都繼承自它。它是 Objective-C 中的兩個(gè)根類(lèi)(rootclass)之一揍堰,另外一個(gè)是 NSProxy.

  • 運(yùn)行時(shí)的實(shí)現(xiàn)方式

根據(jù)上面的說(shuō)法鹏浅,類(lèi)對(duì)象(Class)同樣也算是對(duì)象,我們稱(chēng)之為類(lèi)對(duì)象屏歹。按照我們前面所說(shuō)的所有的對(duì)象都是某個(gè)類(lèi)的實(shí)例隐砸,那么類(lèi)對(duì)象又是什么類(lèi)的實(shí)例呢?

下面實(shí)現(xiàn)一個(gè)簡(jiǎn)單的類(lèi)西采,并用C重寫(xiě)一遍

@interface NyanCat : NSObject {
    int age;
    NSString *name;
}
- (void)nyan;
+ (void)nyan;
@end
 
@implementation NyanCat
- (void)nyan1 {
    printf("instance nyan~");
}
+ (void)nyan2 {
    printf("class nyan~");
}
@end

上面是一個(gè)簡(jiǎn)單的類(lèi)凰萨,有兩個(gè)instance variable,有一個(gè)類(lèi)方法、一個(gè)實(shí)例方法胖眷。

C的大概實(shí)現(xiàn)

//Class的實(shí)際結(jié)構(gòu)
struct _class_t {
    struct _class_t *isa;        //isa指針
    struct _class_t *superclass; //父類(lèi)
    void *cache;
    void *vtable;
    struct _class_ro_t *ro;     //Class包含的信息
};
 
//Class包含的信息
struct _class_ro_t {
    unsigned int flags;
    unsigned int instanceStart;
    unsigned int instanceSize;
    unsigned int reserved;
    const unsigned char *ivarLayout;
    const char *name;                                 //類(lèi)名
    const struct _method_list_t *baseMethods;         //方法列表
    const struct _objc_protocol_list *baseProtocols;  //協(xié)議列表
    const struct _ivar_list_t *ivars;                 //ivar列表
    const unsigned char *weakIvarLayout;
    const struct _prop_list_t *properties;            //屬性列表
};
 
 //NyanCat(meta-class)
struct _class_t OBJC_METACLASS_$_NyanCat  = {
    .isa        = &OBJC_METACLASS_$_NSObject,     //指向根元類(lèi)類(lèi)
    .superclass = &OBJC_METACLASS_$_NSObject,
    .cache      = (void *)&_objc_empty_cache,
    .vtable     = (void *)&_objc_empty_vtable,
    .ro         = &_OBJC_METACLASS_RO_$_NyanCat, //包含了類(lèi)方法等
};
 
//NyanCat(Class)
struct _class_t OBJC_CLASS_$_NyanCat = {
    .isa        = &OBJC_METACLASS_$_NyanCat,   //此處isa指向meta-class
    .superclass = &OBJC_CLASS_$_NSObject,
    .superclass = (void *)&_objc_empty_cache,
    .vtable     = (void *)&_objc_empty_vtable,
    .ro         = &_OBJC_CLASS_RO_$_NyanCat,   //包含了實(shí)例方法 ivar信息等
};
 
typedef struct objc_object NyanCat;   //定義NyanCat類(lèi)型

所有NyanCat的實(shí)例的isa都指向了NyanCat(Class)武通。

NyanCat(Class)是一個(gè)全局變量,其中記錄了類(lèi)名珊搀、成員變量信息冶忱、property信息、protocol信息和實(shí)例方法列表等境析。

NyanCat(Class)的isa指向了全局變量NyanCat(meta-class)囚枪,meta-class里只記錄了類(lèi)名、類(lèi)方法列表等劳淆。

畫(huà)出來(lái)就是這樣:


objc-meta.png

其實(shí)這里meta-class的isa指針也是有指向的链沼,它指向根元類(lèi)。也就是根類(lèi)(NSObject)對(duì)應(yīng)的元類(lèi)沛鸵。

元類(lèi)

我們上面提到括勺,本質(zhì)上 Objective-C 中的類(lèi)也是對(duì)象,它也是某個(gè)類(lèi)的實(shí)例曲掰,這個(gè)類(lèi)我們稱(chēng)之為元類(lèi)(metaclass)疾捍。

因此,我們也可以通過(guò)調(diào)用類(lèi)方法栏妖,比如 [NSObject new]乱豆,給類(lèi)對(duì)象發(fā)送消息。同樣的吊趾,類(lèi)對(duì)象能否響應(yīng)這個(gè)消息也要通過(guò) isa 找到類(lèi)對(duì)象所屬的類(lèi)(元類(lèi))才能知道宛裕。也就是說(shuō),實(shí)例方法是保存在類(lèi)中的趾徽,而類(lèi)方法是保存在元類(lèi)中的续滋。

那元類(lèi)也是對(duì)象嗎?是的話(huà)那它又是什么類(lèi)的實(shí)例呢孵奶?是的,沒(méi)錯(cuò)蜡峰,元類(lèi)也是對(duì)象(元類(lèi)對(duì)象)了袁,元類(lèi)也是某個(gè)類(lèi)的實(shí)例,這個(gè)類(lèi)我們稱(chēng)之為根元類(lèi)(root metaclass)湿颅。不過(guò)载绿,有一點(diǎn)比較特殊,那就是所有的元類(lèi)所屬的類(lèi)都是同一個(gè)根元類(lèi)(當(dāng)然根元類(lèi)也是元類(lèi)油航,所以它所屬的類(lèi)也是根元類(lèi)崭庸,即它本身)。根元類(lèi)指的就是根類(lèi)的元類(lèi),具體來(lái)說(shuō)就是根類(lèi) NSObject 對(duì)應(yīng)的元類(lèi)怕享。

因此执赡,理論上我們也可以給元類(lèi)發(fā)送消息,但是 Objective-C 傾向于隱藏元類(lèi)函筋,不想讓大家知道元類(lèi)的存在沙合。元類(lèi)是為了保持 Objective-C 對(duì)象模型在設(shè)計(jì)上的完整性而引入的,比如用來(lái)保存類(lèi)方法等跌帐,它主要是用來(lái)給編譯器使用的首懈。

說(shuō)了這么多,好像有點(diǎn)暈谨敛,用一張圖來(lái)解釋究履。

object_model.png

由圖所示,當(dāng)向一個(gè)對(duì)象發(fā)送消息時(shí)脸狸,該對(duì)象會(huì)通過(guò)isa找到它的類(lèi)挎袜,然后在它類(lèi)的方法列表中搜尋有沒(méi)有對(duì)應(yīng)的方法,如果沒(méi)有肥惭,就會(huì)沿著superclass的線查找上去盯仪,如果一直沒(méi)有找到,就會(huì)進(jìn)行消息轉(zhuǎn)發(fā)機(jī)制(關(guān)于消息轉(zhuǎn)發(fā)機(jī)制后面我會(huì)專(zhuān)門(mén)寫(xiě)一篇關(guān)于runtime的文章)而向一個(gè)類(lèi)發(fā)送消息時(shí)蜜葱,該類(lèi)對(duì)象會(huì)通過(guò)isa找到它的元類(lèi)全景,然后在該元類(lèi)的方法列表中搜尋有沒(méi)有相應(yīng)的方法,機(jī)制和向?qū)ο蟀l(fā)送消息是一樣的牵囤。

好了爸黄,OC中的類(lèi)與對(duì)象的工作機(jī)制大概就是這樣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末揭鳞,一起剝皮案震驚了整個(gè)濱河市炕贵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌野崇,老刑警劉巖称开,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異乓梨,居然都是意外死亡鳖轰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)扶镀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蕴侣,“玉大人,你說(shuō)我怎么就攤上這事臭觉±ト福” “怎么了辱志?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)狞膘。 經(jīng)常有香客問(wèn)我揩懒,道長(zhǎng),這世上最難降的妖魔是什么客冈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任旭从,我火速辦了婚禮,結(jié)果婚禮上场仲,老公的妹妹穿的比我還像新娘和悦。我一直安慰自己,他們只是感情好渠缕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布鸽素。 她就那樣靜靜地躺著,像睡著了一般亦鳞。 火紅的嫁衣襯著肌膚如雪馍忽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,245評(píng)論 1 299
  • 那天燕差,我揣著相機(jī)與錄音遭笋,去河邊找鬼。 笑死徒探,一個(gè)胖子當(dāng)著我的面吹牛瓦呼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播测暗,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼央串,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了碗啄?” 一聲冷哼從身側(cè)響起质和,我...
    開(kāi)封第一講書(shū)人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稚字,沒(méi)想到半個(gè)月后饲宿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尉共,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年褒傅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袄友。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖霹菊,靈堂內(nèi)的尸體忽然破棺而出剧蚣,到底是詐尸還是另有隱情支竹,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布鸠按,位于F島的核電站礼搁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏目尖。R本人自食惡果不足惜馒吴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瑟曲。 院中可真熱鬧饮戳,春花似錦、人聲如沸洞拨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)烦衣。三九已至歹河,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間花吟,已是汗流浹背秸歧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留衅澈,地道東北人键菱。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像矾麻,于是被迫代替她去往敵國(guó)和親纱耻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

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

  • 原文出處:南峰子的技術(shù)博客 Objective-C語(yǔ)言是一門(mén)動(dòng)態(tài)語(yǔ)言险耀,它將很多靜態(tài)語(yǔ)言在編譯和鏈接時(shí)期做的事放到了...
    _燴面_閱讀 1,229評(píng)論 1 5
  • 本文轉(zhuǎn)載自:http://southpeak.github.io/2014/10/25/objective-c-r...
    idiot_lin閱讀 935評(píng)論 0 4
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉弄喘,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,709評(píng)論 0 9
  • 我們常常會(huì)聽(tīng)說(shuō) Objective-C 是一門(mén)動(dòng)態(tài)語(yǔ)言,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢甩牺?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,192評(píng)論 0 7
  • 山有縫蘑志,河水穿過(guò)。山根處贬派,鄰水有村落急但。 村挨水而落,省道隨河穿梭搞乏。 榆楊下一水的土墻瓦房分別錯(cuò)落波桩,東家鄰西家院時(shí)不...
    山野盲流閱讀 400評(píng)論 13 5