我們都知道衡奥,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)就是這樣:
其實(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)解釋究履。
由圖所示,當(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ī)制大概就是這樣。