NSObject對(duì)象模型解析(上)

原創(chuàng)文章轉(zhuǎn)載請(qǐng)注明出處,謝謝


很早就想講關(guān)于NSObject這個(gè)對(duì)象的模型,其實(shí)網(wǎng)上有不少講這方面知識(shí)的,但是我感覺很多都是千遍一律,而且有一些結(jié)論的并沒有系統(tǒng)的論證,所以我會(huì)從我的理解層面來解析主要分為兩方面,第一篇主要介紹NSObject整體的模型結(jié)構(gòu),第二篇是解析NSObject的一些類方法和實(shí)例方法。所以可能這兩篇分享會(huì)很長(zhǎng),這個(gè)也是為了詳細(xì)的介紹這些知識(shí)。

typedef struct objc_class *Class;
typedef struct objc_object *id;

@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

NSObject實(shí)現(xiàn)了一個(gè)NSObject的protocol,里面包含一個(gè)isa的指針指向一個(gè)objc_class的結(jié)構(gòu)體;而id其實(shí)就是objc_object的結(jié)構(gòu)指針,所以它可以指向任何對(duì)象。

那么objc_class是一個(gè)什么結(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;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    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;
#endif

} OBJC2_UNAVAILABLE;

這個(gè)結(jié)構(gòu)大家肯定不會(huì)陌生,這里重要的點(diǎn)主要包括兩方面的問題:

  • isa和super_class指向的對(duì)象區(qū)別,也就是class和metaclass的區(qū)別(這個(gè)后面要重點(diǎn)詳細(xì)講)
  • objc_ivar_list, objc_method_list, objc_cache, objc_protocol_list幾個(gè)struct的具體的結(jié)構(gòu)問題

接下來就是我們的正題了,到底一個(gè)NSObject對(duì)象編譯后變成什么樣了呢?

首先我們定義如下的兩個(gè)Class:

// AClass.h
@protocol AClassProtocol <NSObject>

- (void)protocolsMethod_AClass;

@end

@interface AClass : NSObject<AClassProtocol>

@property (nonatomic) NSString *name_AClass;

- (void)instanceMethods_AClass;

+ (void)classMethods_AClass;

@end

// AClass.m
@interface AClass() {
    NSString *_address;
}

@end

@implementation AClass

- (void)instanceMethods_AClass {
    
}

- (void)protocolsMethod_AClass {
    
}

+ (void)classMethods_AClass {
    
}

@end


// BClass.h
@interface BClass : AClass

@property (nonatomic) NSString *name_BClass;

- (void)instanceMethods_BClass;

+ (void)classMethods_BClass;

@end

// BClass.m
@implementation BClass
- (void)instanceMethods_BClass {
    
}

+ (void)classMethods_BClass {
    
}

@end

接下來我們把它們編譯成C++文件,我們以AClass.cpp為例,來分析一下文件的組成結(jié)構(gòu):

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;
    const struct _ivar_list_t *ivars;
    const unsigned char *weakIvarLayout;
    const struct _prop_list_t *properties;
};

struct _class_t {
    struct _class_t *isa;
    struct _class_t *superclass;
    void *cache;
    void *vtable;
    struct _class_ro_t *ro;
};

編譯以后的結(jié)構(gòu)和我們之前定義的objc_class稍微有點(diǎn)區(qū)別,但是大致的結(jié)構(gòu)還是一樣的,_class_ro_t中定義了我們之前所說的幾個(gè)struct結(jié)構(gòu)。

static struct _class_ro_t _OBJC_CLASS_RO_$_AClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    0, __OFFSETOFIVAR__(struct AClass, _name), sizeof(struct AClass_IMPL), 
    (unsigned int)0, 
    0, 
    "AClass",
    (const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_AClass,
    (const struct _objc_protocol_list *)&_OBJC_CLASS_PROTOCOLS_$_AClass,
    (const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_AClass,
    0, 
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_AClass,
};

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_AClass __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_AClass,
    0, // &OBJC_CLASS_$_NSObject,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_CLASS_RO_$_AClass,
};

我們的AClass被定義成一個(gè)叫做OBJC_CLASS_$AClass的struct對(duì)象,這里的metaclass和class等等目前都是0,先不要急后面會(huì)初始化的,這里重點(diǎn)先介紹OBJC_CLASS_RO$AClass的結(jié)構(gòu),它就是一個(gè)_class_ro_t的結(jié)構(gòu)體,我們發(fā)現(xiàn)_method_list_t,_objc_protocol_list,_ivar_list_t,_prop_list_t分別在里面被初始化了值,那么我們就來看一下它們對(duì)應(yīng)的結(jié)構(gòu):

// _OBJC_$_INSTANCE_METHODS_AClass
struct _objc_method {
    struct objc_selector * _cmd;
    const char *method_type;
    void  *_imp;
};
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[4];
} _OBJC_$_INSTANCE_METHODS_AClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    4,
    {
    {(struct objc_selector *)"instanceMethods_AClass", "v16@0:8", (void *)_I_AClass_instanceMethods_AClass},
    {(struct objc_selector *)"protocolsMethod_AClass", "v16@0:8", (void *)_I_AClass_protocolsMethod_AClass},
    {(struct objc_selector *)"name", "@16@0:8", (void *)_I_AClass_name},
    {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_AClass_setName_}
    }
};

_OBJC_$__INSTANCE_METHODS_AClass是一個(gè)實(shí)例方法的列表,其中也包括了get/set方法,以及聲明了protocol的方法。而_objc_method的結(jié)構(gòu)其實(shí)就是一個(gè)objc_selector,加上類型type,還有就是一個(gè)imp的指針。(有沒有發(fā)現(xiàn)少了一個(gè)類方法在里面,下面會(huì)介紹原因)

// _OBJC_CLASS_PROTOCOLS_$_AClass
static struct /*_protocol_list_t*/ {
    long protocol_count;  // Note, this is 32/64 bit
    struct _protocol_t *super_protocols[1];
} _OBJC_CLASS_PROTOCOLS_$_AClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    1,
    &_OBJC_PROTOCOL_AClassProtocol
};

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_PROTOCOL_INSTANCE_METHODS_AClassProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"protocolsMethod_AClass", "v16@0:8", 0}}
};

struct _protocol_t _OBJC_PROTOCOL_AClassProtocol __attribute__ ((used, section ("__DATA,__datacoal_nt,coalesced"))) = {
    0,
    "AClassProtocol",
    (const struct _protocol_list_t *)&_OBJC_PROTOCOL_REFS_AClassProtocol,
    (const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_AClassProtocol,
    0,
    0,
    0,
    0,
    sizeof(_protocol_t),
    0,
    (const char **)&_OBJC_PROTOCOL_METHOD_TYPES_AClassProtocol
};

static struct /*_protocol_list_t*/ {
    long protocol_count;  // Note, this is 32/64 bit
    struct _protocol_t *super_protocols[1];
} _OBJC_PROTOCOL_REFS_AClassProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    1,
    &_OBJC_PROTOCOL_NSObject
};

_OBJC_CLASS_PROTOCOLS_$AClass是一個(gè)比較復(fù)雜的結(jié)構(gòu)體,因?yàn)槠南拗粕厦娴拇a我并沒有給全,所有protocol的方法都會(huì)被添加到這個(gè)list里,但是其中還會(huì)包含NSObject protocol的那些方法,也就是_OBJC_PROTOCOL_NSObject中我沒有給出來的那部分,因?yàn)楸容^復(fù)雜,所以我們以后分開單獨(dú)講。

// _OBJC_$_INSTANCE_VARIABLES_AClass

struct _ivar_t {
    unsigned long int *offset;  // pointer to ivar offset location
    const char *name;
    const char *type;
    unsigned int alignment;
    unsigned int  size;
};

static struct /*_ivar_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count;
    struct _ivar_t ivar_list[2];
} _OBJC_$_INSTANCE_VARIABLES_AClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_ivar_t),
    2,
    {
    {(unsigned long int *)&OBJC_IVAR_$_AClass$_address, "_address", "@\"NSString\"", 3, 8},
     {(unsigned long int *)&OBJC_IVAR_$_AClass$_name_AClass, "_name_AClass", "@\"NSString\"", 3, 8}
     }
};

_OBJC_$INSTANCE_VARIABLES_AClass是一個(gè)指向?qū)嵗兞康牧斜?,這里我們會(huì)發(fā)現(xiàn)定義成屬性的name也會(huì)變成一個(gè)實(shí)例變量,其實(shí)是因?yàn)榫幾g器為你自動(dòng)加上了@synthesize,所以變成了_name的一個(gè)實(shí)例變量,_ivar_t定義也是非常的簡(jiǎn)單,包括了name,偏移量,type,size等等。

// _OBJC_$_PROP_LIST_AClass
struct _prop_t {
    const char *name;
    const char *attributes;
};

static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_AClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"name_AClass","T@\"NSString\",N,V_name_AClass"}}
};

_OBJC_$PROP_LIST_AClass很明顯就是屬性的列表,即聲明了@property的變量。

最后就是關(guān)于objc_cache的一些介紹,其實(shí)我們每次objc_msgsend一個(gè)方法的時(shí)候都是去遍歷_method_list_t的,這樣對(duì)于一些常用的方法就會(huì)導(dǎo)致效率不高,這個(gè)時(shí)候我們就會(huì)把常用的一些方法cache下來,通過key-value的map方式,將name對(duì)應(yīng)到各自的imp,這樣就大大提高了效率。


好,講到這里,我們已經(jīng)解決了我們的其中一個(gè)問題,接下來就是要講最重要的問題了,OC中的metaclass到底是個(gè)什么東西,了解過的同學(xué)應(yīng)該都知道,isa指針指向的其實(shí)就是一個(gè)metaclass,metaclass其實(shí)是一個(gè)類對(duì)象的類,可能這樣解釋會(huì)很抽象,確實(shí)不容易理解,我們通過實(shí)際的代碼結(jié)構(gòu)來驗(yàn)證這個(gè)問題。

// AClass.cpp
static void OBJC_CLASS_SETUP_$_AClass(void ) {
    OBJC_METACLASS_$_AClass.isa = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_AClass.superclass = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_AClass.cache = &_objc_empty_cache;
    OBJC_CLASS_$_AClass.isa = &OBJC_METACLASS_$_AClass;
    OBJC_CLASS_$_AClass.superclass = &OBJC_CLASS_$_NSObject;
    OBJC_CLASS_$_AClass.cache = &_objc_empty_cache;
}

以上就是我們之前在前面編譯后的AClass對(duì)象,OBJC_CLASS_$AClass的isa指針是指向一個(gè)OBJC_METACLASS_$AClass,而superclass是指向OBJC_CLASS_$NSObject;那我們來看一下OBJC_METACLASS_$AClass的定義又是什么樣的呢?

extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_AClass __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_NSObject,
    0, // &OBJC_METACLASS_$_NSObject,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_METACLASS_RO_$_AClass,
};

static struct _class_ro_t _OBJC_METACLASS_RO_$_AClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    1, sizeof(struct _class_t), sizeof(struct _class_t), 
    (unsigned int)0, 
    0, 
    "AClass",
    (const struct _method_list_t *)&_OBJC_$_CLASS_METHODS_AClass,
    0, 
    0, 
    0, 
    0, 
};

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_AClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"classMethods_AClass", "v16@0:8", (void *)_C_AClass_classMethods_AClass}}
};

看了上面的定義我們發(fā)現(xiàn),之前我們沒有在前面的_method_list中找到的那個(gè)類方法原來出現(xiàn)在這里,也就說OBJC_METACLASS$AClass里其實(shí)包含了類方法。這個(gè)就驗(yàn)證了網(wǎng)上說的一個(gè)結(jié)論:

  • 當(dāng)你向一個(gè)對(duì)象發(fā)送消息時(shí),runtime會(huì)在這個(gè)對(duì)象所屬的那個(gè)類的方法列表中查找。
  • 當(dāng)你向一個(gè)類發(fā)送消息時(shí),runtime會(huì)在這個(gè)類的meta-class的方法列表中查找。

OBJC_METACLASS$AClass的isa又是指向OBJC_METACLASS_$NSObject,superclass也是指向OBJC_METACLASS_$NSObject。好,我們接下來把BClass中的isa和superclass也聯(lián)系起來:

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_BClass __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_BClass,
    0, // &OBJC_CLASS_$_AClass,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_CLASS_RO_$_BClass,
};
static void OBJC_CLASS_SETUP_$_BClass(void ) {
    OBJC_METACLASS_$_BClass.isa = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_BClass.superclass = &OBJC_METACLASS_$_AClass;
    OBJC_METACLASS_$_BClass.cache = &_objc_empty_cache;
    OBJC_CLASS_$_BClass.isa = &OBJC_METACLASS_$_BClass;
    OBJC_CLASS_$_BClass.superclass = &OBJC_CLASS_$_AClass;
    OBJC_CLASS_$_BClass.cache = &_objc_empty_cache;
}

我們把這些信息串聯(lián)在一起,可以得出如下的一張分析圖:

pic_three.jpg

是不是發(fā)現(xiàn)這個(gè)圖在哪里好像見到過,沒錯(cuò)就是介紹metaclass原理的一張圖:


pic_two.jpg

但是仔細(xì)比較還是會(huì)發(fā)現(xiàn),有一些不同的地方,就是關(guān)于NSObject的superclass到底是什么,以及Meta_NSObject的isa和superclass是什么,因?yàn)閺奈覀冎暗膶?shí)驗(yàn)?zāi)P褪菬o法得出的,我試圖編譯NSObject.mm的源碼,但是由于環(huán)境的問題失敗了,所以我們需要通過其他的手段來驗(yàn)證圖上的解釋是否是正確的,我們通過在runtime的時(shí)候動(dòng)態(tài)生成一個(gè)class,它繼承自BClass,如下:

 Class newClass =
 objc_allocateClassPair([BClass class], "BClassSubClass", 0);
 class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:");
 objc_registerClassPair(newClass);
 id instanceOfNewClass=[[newClass alloc] init];
 [instanceOfNewClass performSelector:@selector(report)];

void ReportFunction(id self, SEL _cmd) {
    Class superClazz = [self superclass];
    while (superClazz) {
        NSLog(@"superclass %p %@", superClazz, superClazz);
        superClazz = [superClazz superclass];
    }
    NSLog(@"%p %@", superClazz, superClazz);

    Class metaClass = object_getClass([self class]);
    while (metaClass) {
        NSLog(@"%p is metaClass : %d", metaClass, class_isMetaClass(metaClass));
        if (metaClass != object_getClass(metaClass)) {
            metaClass = object_getClass(metaClass);
        } else {
            break;
        }
    }
    NSLog(@"BClass's class is %p", [BClass class]);
    NSLog(@"BClass's meta class is %p", object_getClass([BClass class]));
    NSLog(@"AClass's class is %p", [AClass class]);
    NSLog(@"AClass's meta class is %p", object_getClass([AClass class]));
    NSLog(@"NSObject's class is %p", [NSObject class]);
    NSLog(@"NSObject's superclass is %p", [[NSObject class] superclass]);
    NSLog(@"NSObject's meta class is %p", object_getClass([NSObject class]));
    NSLog(@"NSObject's meta superclass is %p", [object_getClass([NSObject class]) superclass]);
}

// out put

2016-06-23 00:03:39.079 InitDemo[1775:97046] superclass 0x100002908 BClass
2016-06-23 00:03:39.079 InitDemo[1775:97046] superclass 0x1000028e0 AClass
2016-06-23 00:03:39.080 InitDemo[1775:97046] superclass 0x7fff79ea20f0 NSObject
2016-06-23 00:03:39.080 InitDemo[1775:97046] 0x0 (null)
2016-06-23 00:03:39.080 InitDemo[1775:97046] 0x100508dc0 is metaClass : 1
2016-06-23 00:03:39.080 InitDemo[1775:97046] 0x7fff79ea2118 is metaClass : 1
2016-06-23 00:03:39.080 InitDemo[1775:97046] BClass's class is 0x100002908
2016-06-23 00:03:39.080 InitDemo[1775:97046] BClass's meta class is 0x100002930
2016-06-23 00:03:39.080 InitDemo[1775:97046] AClass's class is 0x1000028e0
2016-06-23 00:03:39.080 InitDemo[1775:97046] AClass's meta class is 0x1000028b8
2016-06-23 00:03:39.080 InitDemo[1775:97046] NSObject's class is 0x7fff79ea20f0
2016-06-23 00:03:39.081 InitDemo[1775:97046] NSObject's superclass is 0x0
2016-06-23 00:03:39.081 InitDemo[1775:97046] NSObject's meta class is 0x7fff79ea2118
2016-06-23 00:03:39.081 InitDemo[1775:97046] NSObject's meta superclass is 0x7fff79ea20f0

首先告訴大家的是object_getClass函數(shù)其實(shí)是去獲取class的isa指針;好,我們發(fā)現(xiàn) NSObject's superclass is 0x0,確實(shí)和圖上顯示一樣,NSObject的superclass是指向nil的,而且NSObject's meta superclass的地址是和NSObject's class相同,代表meta_NSObject的superclass是指向NSObject的;而meta_NSObject的metaclass的地址和meta_NSObject的地址都是0x7fff79ea2118,所以meta_NSObject的metaclass是指向自己的。這樣我們就可以把我們自己的圖補(bǔ)充完整。

pic_one.jpg

所以最后我們可以得出來的結(jié)論就是:

  • 每個(gè)Class都有一個(gè)isa指針指向一個(gè)唯一的Meta Class
  • 每一個(gè)Meta Class的isa指針都指向最上層的Meta Class(圖中的NSObject的Meta Class)
  • 最上層的Meta Class的isa指針指向自己,形成一個(gè)回路
  • 每一個(gè)Meta Class的super class指針指向它原本Class的 Super Class的Meta Class, 但是最上層的Meta Class的 Super Class指向NSObject Class本身.
  • 最上層的NSObject Class的super class指向 nil

好了,到現(xiàn)在我們所要說明的兩方面問題都已經(jīng)解釋了,關(guān)于NSObject的一些方法,我會(huì)在后面在來分析。
最后提一點(diǎn)就是關(guān)于self和[self class]的區(qū)別,self 是指向于一個(gè)objc_object結(jié)構(gòu)體的首地址, 返回的是objc_class結(jié)構(gòu)體的首地址,也就是self->isa的值。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容