本文章轉(zhuǎn)自Objective-C 中的類和對象
Objective-C的runtime是開源的止状,源碼可以在蘋果官網(wǎng)下載到:objc4。
好了阳仔,下面正文:
1.id和Class的定義
runtime里面忧陪,聲明了id
和Class
的類型,簡化一下如下:
struct objc_class {
struct objc_class *isa;
};
struct objc_object {
struct objc_class *isa;
};
typedef struct objc_class *Class; //類 (class object)
typedef struct objc_object *id; //對象 (instance of class)
在objc中近范,id
代表了一個對象嘶摊。根據(jù)上面的聲明,凡是首地址是*isa
的struct指針评矩,都可以被認為是objc中的對象叶堆。運行時可以通過isa
指針,查找到該對象是屬于什么類(Class)斥杜。
2.運行時的實現(xiàn)方式
根據(jù)上面的說法虱颗,類對象(Class)同樣也算是對象,那它的isa
又是指向了什么呢蔗喂?為了了解這些東西是怎么回事忘渔,這里寫一個簡單的類NyanCat,并且用C重寫一遍缰儿,看看編譯器在底層到底是如何實現(xiàn)的畦粮。
@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
上面是一個簡單的類,有兩個instance variable乖阵,有一個類方法宣赔、一個實例方法。
clang -rewrite-objc NyanCat.m
在終端執(zhí)行上面這一條語句瞪浸,讓clang將該類重寫為cpp代碼儒将,我們就能查看到大概底層的實現(xiàn)機制了(實際編譯的文件和這個會有些出入,不同目標架構(gòu)和不同版本clang也會有不同..權(quán)且當參考了)对蒲。
rewrite后的代碼基本是純C的钩蚊,稍微整理一下贡翘,可以提取出下面這些信息:
//Class的實際結(jié)構(gòu)
struct _class_t {
struct _class_t *isa; //isa指針
struct _class_t *superclass; //父類
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; //類名
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,
.superclass = &OBJC_METACLASS_$_NSObject,
.cache = (void *)&_objc_empty_cache,
.vtable = (void *)&_objc_empty_vtable,
.ro = &_OBJC_METACLASS_RO_$_NyanCat, //包含了類方法等
};
//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, //包含了實例方法 ivar信息等
};
typedef struct objc_object NyanCat; //定義NyanCat類型
//更詳細的不貼代碼了..
所有NyanCat的實例的isa
都指向了NyanCat(Class)。
NyanCat(Class)是一個全局變量两疚,其中記錄了類名床估、成員變量信息含滴、property信息诱渤、protocol信息和實例方法列表等。
NyanCat(Class)的isa指向了全局變量NyanCat(meta-class)谈况,meta-class里只記錄了類名勺美、類方法列表等。
畫出圖來就是這樣:
舉例來說一下:
NyanCat *cat = [[NyanCat alloc] init];
[cat nyan1];
向cat (instance) 發(fā)送消息nyan1時碑韵,運行時會通過isa指針查找到NyanCat(Class)赡茸,這里保存著本類中定義的實例方法的指針。
[NyanCat nyan2];
向NyanCat(Class)發(fā)送消息nyan2時祝闻,運行時會通過isa查找到NyanCat(meta-class)占卧,這里保存著本類中定義的類方法的指針诗力。
運行時如何利用Class和meta-class來實現(xiàn)動態(tài)消息的献宫,以后在記吧~
3.類的繼承
在_class_t里面除嘹,第二個成員是superclass强挫,很明顯這個指針指向了它的父類裳凸。運行時可以通過isa和superclass獲取一個類在繼承樹上的完整信息农曲。
為了說明方便膝宁,這里把上面的例子稍微改一下:NyanCat : Cat : NSObject 這樣一個繼承樹靖榕,畫出圖來就是這樣子的:
如上面圖中蓖谢,跟隨黑線捂蕴,可以看到isa的指向。運行時闪幽,每個對象的isa都不為空啥辨,這樣只要是一個id類型的對象,runtime都可以通過訪問首地址偏移(isa)來獲取該對象的信息了盯腌。
上圖中跟隨綠線委可,可以看到superclass的指向。當運行時在搜尋方法腊嗡、ivar信息時着倾,如果沒有找到信息,則會沿superclass的線查找上去燕少,最終NSObject(根類)的superclass是nil卡者。
如果自己定義了一個根類(比如NSProxy),則這個根類會替換圖中NSObject的位置客们。
為了驗證上面的說法崇决,可以敲一下代碼看看:
#import "NyanCat.h"
#import <objc/runtime.h>
#import <objc/objc.h>
void test() {
NyanCat *cat = [[NyanCat alloc] init];
Class cls = object_getClass(cat); //NyanCat(Class)
class_getName(cls); //"NyanCat"
class_isMetaClass(cls); //NO
Class meta = object_getClass(cls); //NyanCat(meta-class)
class_getName(meta); //"NyanCat"
class_isMetaClass(cls); //YES
Class meta_meta = object_getClass(meta); //NSObject(meta-class)
class_getName(meta_meta); //"NSObject"
class_isMetaClass(meta_meta); //YES
}
最后吐嘈一下:平時開發(fā)時材诽,meta-class基本是用不著接觸的,superclass指針無法訪問恒傻,isa指針可能稍后也會隱藏起來(蘋果的動作真多)脸侥。。所以上面說得這些盈厘,了解一下就好~~