OC對(duì)象的本質(zhì)<二> 實(shí)例對(duì)象,類(lèi)對(duì)象,元類(lèi)對(duì)象

OC對(duì)象的本質(zhì)<一>

OC對(duì)象的分類(lèi)

OC對(duì)象可以分為三類(lèi),分別是實(shí)例對(duì)象,類(lèi)對(duì)象,元類(lèi)對(duì)象。

實(shí)例對(duì)象(instance對(duì)象)

instance對(duì)象是通過(guò)類(lèi)alloc出來(lái)的對(duì)象,每次調(diào)用alloc都會(huì)產(chǎn)生新的instance對(duì)象。

  • instance對(duì)象在內(nèi)存中存儲(chǔ)的信息
    isa指針(因?yàn)閹缀跛械膶?duì)象都繼承自NSObject,而NSObject對(duì)象的結(jié)構(gòu)體重就是一個(gè)isa指針)
    其他成員變量(注意這里存儲(chǔ)的是成員變量的值)
    我們看一下Demo:
@interface Person:NSObject
{
    @public
    int _age;
}
@property (nonatomic, assign)int height;
@end

@implementation Person
@end

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        Person *p1 = [[Person alloc] init];
        p1->_age = 3;
        
        Person *p2 = [[Person alloc] init];
        p2->_age = 4;
        
        return 0;
    }
}

那么這個(gè)時(shí)候首先為p1實(shí)例對(duì)象分配存儲(chǔ)空間,先存儲(chǔ)isa這個(gè)指針,然后存儲(chǔ)成員變量_age=3;對(duì)于p2實(shí)例對(duì)象也是一樣的。由于p1指針指向Person實(shí)例對(duì)象,也就是指向Person實(shí)例對(duì)象在內(nèi)存中的首地址,而Person實(shí)例對(duì)象中存儲(chǔ)的第一個(gè)成員變量是isa指針,所以p1指針指向的地址就是存儲(chǔ)isa指針的地址。


17703D99-4D1C-45D2-85C2-5E053C2FA656.png

類(lèi)對(duì)象(Class對(duì)象)

類(lèi)對(duì)象的獲取方式:

NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = object_getClass(object1);
Class objectClass4 = object_getClass(object2);
Class objectClass5 = [NSObject class];

一個(gè)類(lèi)的類(lèi)對(duì)象在內(nèi)存中是唯一的,這就說(shuō)明我們通過(guò)這五種方式所創(chuàng)建的類(lèi)方法是同一個(gè)對(duì)象。我們通過(guò)打印這5個(gè)類(lèi)對(duì)象的內(nèi)存地址來(lái)驗(yàn)證一下:

NSLog(@"%p \n %p \n %p \n %p \n %p \n", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5);

打印結(jié)果:

 0x10b0a6ea8 
 0x10b0a6ea8 
 0x10b0a6ea8 
 0x10b0a6ea8 
 0x10b0a6ea8

這也就驗(yàn)證了這五個(gè)對(duì)象是同一個(gè)對(duì)象,也就是一個(gè)類(lèi)只有一個(gè)類(lèi)對(duì)象。
既然每個(gè)類(lèi)的類(lèi)對(duì)象只有唯一一個(gè),那么在每個(gè)類(lèi)的類(lèi)對(duì)象中會(huì)存儲(chǔ)什么東西呢?肯定是存放那么只需要存儲(chǔ)一份的東西,不會(huì)是像成員變量的值一樣,每個(gè)實(shí)例對(duì)象都可以有不同的成員變量值。

  • Class對(duì)象在內(nèi)存中存儲(chǔ)的信息主要包括:
    isa指針
    superclass指針
    類(lèi)的屬性信息(@property),類(lèi)的對(duì)象方法信息(instance method)
    類(lèi)的協(xié)議信息(@protocol),類(lèi)的成員變量信息(ivars,類(lèi)型,名稱(chēng)等)


    類(lèi)對(duì)象包含的信息.png

元類(lèi)對(duì)象

  • 元類(lèi)對(duì)象的獲取方法
//我們?cè)趏bject_getClass()方法中傳入類(lèi)對(duì)象就得到了元類(lèi)對(duì)象,每個(gè)類(lèi)的元類(lèi)對(duì)象只有一個(gè),所以objectMetaClass1和objectMetaClass2是同一個(gè)對(duì)象
 Class objectMetaClass1 = object_getClass([NSObject class]);
 Class objectMetaClass2 = object_getClass(objectClass1);

那么元類(lèi)對(duì)象中存放的是什么信息呢?大家想一下實(shí)例對(duì)象和元類(lèi)對(duì)象還有什么信息漏了就能明白元類(lèi)信息中包含什么信息了。

  • meta-Class對(duì)象中包含的信息
    isa指針
    superclass指針
    類(lèi)的類(lèi)方法信息


    元類(lèi)對(duì)象包含的信息.png
  • class_isMetaClass()
    class_isMetaClass()判斷傳進(jìn)去的對(duì)象是否是元類(lèi)對(duì)象。
    BOOL result = class_isMetaClass(objectMetaClass1);

isa指針

我們先看一個(gè)Person類(lèi):

@interface Person:NSObject<NSCopying>
{
    @public
    int _age;
}
@property (nonatomic, assign)int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end

@implementation Person
- (void)personInstanceMethod{
    
    
}
+ (void)personClassMethod{
    
    
}

- (id)copyWithZone:(NSZone *)zone{
    
    return nil;
}

這個(gè)Person類(lèi)有成員變量,屬性,有遵守的協(xié)議,實(shí)例方法,類(lèi)方法。首先我們通過(guò)實(shí)例對(duì)象調(diào)用實(shí)例方法:

Person *person = [[Person alloc] init];
[person personClassMethod];

[Person personClassMethod]這句代碼在底層的實(shí)現(xiàn)一定是objc_msgSend(person,@selector(personInstanceMethod))。這里就有一個(gè)問(wèn)題了,我們是給實(shí)例對(duì)象發(fā)消息,調(diào)用實(shí)例方法,可以實(shí)例對(duì)象中沒(méi)有實(shí)例方法的信息呀。同樣的,當(dāng)我們調(diào)用類(lèi)對(duì)象的時(shí)候[Person personInstanceMethod]這句話(huà)在底層的實(shí)現(xiàn)時(shí)一定是轉(zhuǎn)化為objc_msgSend([Person class],@selector(personClassMethod))這樣,也就是給一個(gè)類(lèi)對(duì)象發(fā)送消息,調(diào)用類(lèi)方法。但是類(lèi)方法是在元類(lèi)對(duì)象里面,不在類(lèi)對(duì)象中呀,這是怎么調(diào)用的呢?這時(shí)isa指針就派上用場(chǎng)了。
Person實(shí)例對(duì)象的isa指針指向Person類(lèi)對(duì)象,Person類(lèi)對(duì)象的isa指針指向Person元類(lèi)對(duì)象。

  • 當(dāng)我們調(diào)用對(duì)象方法時(shí),首先通過(guò)實(shí)例對(duì)象中的isa指針找到類(lèi)對(duì)象,然后獲得類(lèi)對(duì)象中的實(shí)例方法信息。
  • 當(dāng)我們調(diào)用類(lèi)方法時(shí),首先通過(guò)類(lèi)對(duì)象的isa指針找到元類(lèi)對(duì)象,再找到元類(lèi)對(duì)象中的類(lèi)方法信息,實(shí)現(xiàn)調(diào)用。

superclass指針

類(lèi)對(duì)象的superclass指針

我們創(chuàng)建一個(gè)子類(lèi)Student類(lèi)繼承自Person類(lèi)。

@interface Student:Person <NSCoding>
{
    @public
    int _weight;
}
@property (nonatomic, assign)int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;

@end

@implementation Student
- (void)studentInstanceMethod{
    
}

+ (void)studentClassMethod{
    
}
- (id)initWithCoder:(NSCoder *)aDecoder{
    
    return nil;
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
    
}
@end
![![85F2CFF1-F8EF-438A-B32D-CF65BCF10A5A.png](https://upload-images.jianshu.io/upload_images/5796542-fdab2d71d7b42f54.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://upload-images.jianshu.io/upload_images/5796542-7e9a426e8fb01d06.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

我們看一下student實(shí)例對(duì)象調(diào)用實(shí)例方法:

Student *student = [[Student alloc] init];
[student studentInstanceMethod];

這個(gè)過(guò)程我們已經(jīng)很清楚了,就是通過(guò)student實(shí)例對(duì)象的isa指針來(lái)找到Student的類(lèi)對(duì)象,然后在類(lèi)對(duì)象中找到實(shí)例方法,完成調(diào)用。那么如果student實(shí)例對(duì)象調(diào)用的是父類(lèi)Person類(lèi)的實(shí)例方法呢?[student personInstanceMethod];這個(gè)調(diào)用過(guò)程又是怎樣的呢?- (void)personInstanceMethod這個(gè)類(lèi)方法肯定是存放在Person類(lèi)的類(lèi)方法里面的。這個(gè)時(shí)候就是superclass指針發(fā)揮作用的時(shí)候了。
student實(shí)例對(duì)象首先通過(guò)其isa指針找到自己的類(lèi)對(duì)象,然后Student類(lèi)對(duì)象查找自己有沒(méi)有存儲(chǔ)- (void)personInstanceMethod這個(gè)實(shí)例方法,發(fā)現(xiàn)自己并沒(méi)有這個(gè)實(shí)例方法的信息,于是就通過(guò)自己的superclass指針來(lái)找到父類(lèi)的類(lèi)對(duì)象也就是Person類(lèi)對(duì)象,Person類(lèi)對(duì)象查看自己有沒(méi)有存儲(chǔ)- (void)personInstanceMethod這個(gè)實(shí)例方法的信息,結(jié)果找到了這個(gè)實(shí)例方法的信息,至此student實(shí)例對(duì)象(^-^)也就獲取了- (void)personInstanceMethod實(shí)例方法的信息。
類(lèi)對(duì)象的superclass指針指向父類(lèi)的類(lèi)對(duì)象

元類(lèi)對(duì)象的superclass指針

85F2CFF1-F8EF-438A-B32D-CF65BCF10A5A.png

首先我們來(lái)看Student類(lèi)對(duì)象調(diào)用自己的類(lèi)方法:

[Student studentClassMethod];

這個(gè)調(diào)用過(guò)程應(yīng)該已經(jīng)很清楚了,Student類(lèi)的類(lèi)方法信息是存儲(chǔ)在元類(lèi)對(duì)象中的。Student類(lèi)對(duì)象首先通過(guò)自己的isa指針找到Student元類(lèi)對(duì)象,Student元類(lèi)對(duì)象查看自己有沒(méi)有studentClassMethod這個(gè)類(lèi)方法的信息,查看后發(fā)現(xiàn)有,就傳給類(lèi)對(duì)象。那么如果要調(diào)用父類(lèi)Person類(lèi)的類(lèi)方法呢?

[Student personClassMethod];

首先Student類(lèi)對(duì)象通過(guò)自己的isa指針找到Student元類(lèi)對(duì)象,Student元類(lèi)對(duì)象查看自己有沒(méi)有personClassMethod這個(gè)類(lèi)方法的信息,查找后沒(méi)有就利用自己的superclass指針找到父類(lèi)的元類(lèi)對(duì)象,也就是Person類(lèi)的元類(lèi)對(duì)象,Person類(lèi)的元類(lèi)對(duì)象查看后發(fā)現(xiàn)自己有personClassMethod這個(gè)類(lèi)方法的信息,至此Student類(lèi)對(duì)象就找到了personClassMethod這個(gè)類(lèi)方法的信息。
元類(lèi)對(duì)象的superclass指針指向父類(lèi)的元類(lèi)對(duì)象。

isa,superclass總結(jié)

放上一張經(jīng)典的總結(jié)圖

630FEC40-DF9E-4755-80CA-D65810CB3DF1.png

總結(jié)起來(lái)就是:
實(shí)例對(duì)象的isa指針指向該類(lèi)的類(lèi)對(duì)象。
類(lèi)對(duì)象的isa指針該類(lèi)的元類(lèi)對(duì)象。
元類(lèi)對(duì)象的isa指針指向基類(lèi)的元類(lèi)對(duì)象。
類(lèi)對(duì)象的superclass指針指向父類(lèi)的類(lèi)對(duì)象
元類(lèi)對(duì)象的superclass指向父類(lèi)的元類(lèi)對(duì)象(基類(lèi)除外)
基類(lèi)的元類(lèi)對(duì)象的superclass指向基類(lèi)的類(lèi)對(duì)象。

isa指針的一個(gè)小細(xì)節(jié)

前面已經(jīng)說(shuō)了實(shí)例對(duì)象的isa指針指向類(lèi)對(duì)象,類(lèi)對(duì)象的isa指向元類(lèi)對(duì)象。所以實(shí)例對(duì)象的isa指針的值就是類(lèi)對(duì)象的地址值,類(lèi)對(duì)象的isa指針的值就是元類(lèi)對(duì)象的地址值。我們打印看一下結(jié)果:

Person *person = [[Person alloc] init];
Class personClass = [Person class];
Class personMetaClass = object_getClass(personClass);
NSLog(@"%p, %p, %p", person, personClass, personMetaClass);

打印結(jié)果:

Student[1372:63404] 0x600000008390, 0x10e7a4130, 0x10e7a4108

然后我們?cè)俅騻€(gè)斷點(diǎn),查看一下各個(gè)對(duì)象的isa指針值。

1697CA48-0D78-4273-9B39-86D3581DF4DA.png

我們?cè)谡{(diào)試框中輸入p person->isa發(fā)現(xiàn)打印的是$0 = Person,這并不是我們想要的,我們輸入p (long)person->is,這下打印出想要的結(jié)果了$1 = 4537860400,但是這是10進(jìn)制表示,我們需要轉(zhuǎn)化為16進(jìn)制,再輸入p/x (long)person->isa,打印得到16進(jìn)制結(jié)果:$2 = 0x000000010e7a4130。這也就驗(yàn)證了實(shí)例對(duì)象的isa指針指向類(lèi)對(duì)象。
然后我們?cè)囍蛴☆?lèi)對(duì)象的isa指針的值:
p/x personClass->isa
打印結(jié)果是:
error: member reference base type 'Class' is not a structure or union所以這條路行不通了。
我們已經(jīng)知道了這個(gè)personClass這個(gè)類(lèi)對(duì)象中第一個(gè)成員變量一定是isa指針,所以我們可以自己創(chuàng)建一個(gè)結(jié)構(gòu)體:

struct pd_objc_class{
    
    Class isa
};

然后我們把personClass這個(gè)類(lèi)對(duì)象強(qiáng)制轉(zhuǎn)化成pd_objc_class結(jié)構(gòu)體類(lèi)型:

 struct pd_objc_class *personClass2 = (__bridge struct pd_objc_class *)(personClass);

然后再通過(guò)p/x personClass2->isa打印isa指針的值:
$0 = 0x000000010583f108,同時(shí)我們從一開(kāi)始的打印結(jié)果可以找到personMetaClass對(duì)象的地址為0x000000010583f108。
這樣也就證明了類(lèi)對(duì)象的isa指針是指向元類(lèi)對(duì)象的。

驗(yàn)證實(shí)例對(duì)象,類(lèi)對(duì)象,元類(lèi)對(duì)象的結(jié)構(gòu)

類(lèi)對(duì)象和元類(lèi)對(duì)象的類(lèi)型都是Class類(lèi)型,所以本質(zhì)上來(lái)講它們的結(jié)構(gòu)是一致的。那么我們只需要搞清楚這個(gè)Class類(lèi)型的結(jié)構(gòu)就可以搞清楚類(lèi)對(duì)象和元類(lèi)對(duì)象的結(jié)構(gòu)了。
按住command點(diǎn)擊Class查看結(jié)構(gòu):

typedef struct objc_class *Class;

可以看到這是一個(gè)obkc_class類(lèi)型的結(jié)構(gòu)體指針。然后我們繼續(xù)點(diǎn)擊進(jìn)objc_class查看:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

但是我們看到這個(gè)是條件編譯,#if !OBJC2也就是如果不是OBJC2就編譯,但是現(xiàn)在就是OBJC2,所以這段條件代碼不會(huì)編譯。因此這個(gè)代碼就不足以作為參考。那么我們只好從源碼中查看objc_class的結(jié)構(gòu)。
我們?cè)谠创a中搜索objc_class,從objc-runtime-new.h中找到了objc_class的結(jié)構(gòu):

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
}

可以看到,objc_class這個(gè)結(jié)構(gòu)體是繼承自objc_object,我們點(diǎn)進(jìn)objc_object結(jié)構(gòu)體中看看,可以看到

struct objc_object {
private:
    isa_t isa;
}

里面只有一個(gè)成員變量isa指針。所以objc_class這個(gè)結(jié)構(gòu)體的結(jié)構(gòu)就等價(jià)于:

   struct objc_class : objc_object {
    void *isa;;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
}

第一個(gè)成員變量是isa指針,第二個(gè)是superclass指針。第三個(gè)cache是和方法的緩存有關(guān)的。第四個(gè)bits先不管。第五個(gè)是一個(gè)方法,返回值是class_rw_t類(lèi)型的,我們點(diǎn)進(jìn)class_rw_t看看它的結(jié)構(gòu):

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;//方法列表
    property_array_t properties;//屬性列表
    protocol_array_t protocols;//協(xié)議列表
}

然后我們?cè)冱c(diǎn)進(jìn)class_ro_t看看它的結(jié)構(gòu):

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;//類(lèi)名
    method_list_t * baseMethodList;//方法列表
    protocol_list_t * baseProtocols;//協(xié)議列表
    const ivar_list_t * ivars;//成員變量列表

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;//屬性列表

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

從這個(gè)角度確實(shí)可以證明objc_class的結(jié)構(gòu)中有isa指針,superclass指針,方法列表,屬性列表,成員變量列表,協(xié)議列表等。


2874E11F-27F3-4E2B-8276-443CC9CD3A24.png

通過(guò)轉(zhuǎn)化為C++的源碼來(lái)證實(shí)實(shí)例對(duì)象,類(lèi)對(duì)象,元類(lèi)對(duì)象的結(jié)構(gòu)

類(lèi)對(duì)象

@interface Person:NSObject<NSCopying>
{
    @public
    int _age;
}
@property (nonatomic, assign)int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end

@implementation Person
- (void)personInstanceMethod{  
}
+ (void)personClassMethod{   
}
- (id)copyWithZone:(NSZone *)zone{
    return nil;
}

@end

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        Person *person = [[Person alloc] init];
        person->_age = 10;
        person.no = 3;
        Class personClass = [Person class];
        Class personMetaClass = object_getClass(personClass);
 
        return 0;
    }
}

我們把person類(lèi)的代碼轉(zhuǎn)為C++的源碼:
我們找到class_t類(lèi)型的OBJC_CLASS$_Person這個(gè)結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體就死類(lèi)對(duì)象的結(jié)構(gòu)。

struct _class_t OBJC_CLASS_$_Person __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_Person,
    0, // &OBJC_CLASS_$_NSObject,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_CLASS_RO_$_Person,

我們找到_class_t這個(gè)結(jié)構(gòu)體,查看結(jié)構(gòu)

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

我們看到其中第一個(gè)成員是isa指針,第二個(gè)成員是superclass指針,cache和vtable我們先不管。接著我們看到_class_ro_t這個(gè)結(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;
};

這個(gè)結(jié)構(gòu)體我們前面見(jiàn)多過(guò),應(yīng)該已經(jīng)比較熟悉了。然后我們找到類(lèi)對(duì)象中這個(gè)結(jié)構(gòu)的實(shí)現(xiàn):

static struct _class_ro_t _OBJC_CLASS_RO_$_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    0, 
    __OFFSETOFIVAR__(struct Person, _age), 
    sizeof(struct Person_IMPL), 
    (unsigned int)0, 
    0, 
    "Person",
    (const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_Person,
    (const struct _objc_protocol_list *)&_OBJC_CLASS_PROTOCOLS_$_Person,
    (const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_Person,
    0, 
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person,
};
  • 我們可以看到instanceSize對(duì)應(yīng)的是sizeof(struct Person_IMPL)。
  • name也就是類(lèi)名,對(duì)應(yīng)的是"Person"。
  • baseMethods對(duì)應(yīng)的是OBJC$_INSTANCE_METHODS_Person這個(gè)結(jié)構(gòu)體,我們找到這個(gè)結(jié)構(gòu)體的實(shí)現(xiàn):
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_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    4,
    {{(struct objc_selector *)"personInstanceMethod", "v16@0:8", (void *)_I_Person_personInstanceMethod},
    {(struct objc_selector *)"copyWithZone:", "@24@0:8^{_NSZone=}16", (void *)_I_Person_copyWithZone_},
    {(struct objc_selector *)"no", "i16@0:8", (void *)_I_Person_no},
    {(struct objc_selector *)"setNo:", "v20@0:8i16", (void *)_I_Person_setNo_}}
};

我們通過(guò)INSTANCE_METHODS這個(gè)名字知道這個(gè)里面存放的是實(shí)例方法,通過(guò)其初始化可以看到,有四個(gè)實(shí)例方法,方法名分別是"personInstanceMethod";"copyWithZone:";"no";"setNo:"。這是符合實(shí)際的。

  • baseProtocols根據(jù)名字應(yīng)該是存放的協(xié)議信息,它是用OBJC_CLASS_PROTOCOLS$_Person這個(gè)結(jié)構(gòu)體初始化的。我們找到這個(gè)結(jié)構(gòu)體的實(shí)現(xiàn):
static struct /*_protocol_list_t*/ {
    long protocol_count;  // Note, this is 32/64 bit
    struct _protocol_t *super_protocols[1];
} _OBJC_CLASS_PROTOCOLS_$_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    1,
    &_OBJC_PROTOCOL_NSCopying
};

可以看到,協(xié)議數(shù)量是1,協(xié)議是_OBJC_PROTOCOL_NSCopying,通過(guò)名稱(chēng)我們得知是NSCopying,這里不再展開(kāi)。

  • ivars是變量的意思,其對(duì)應(yīng)的是OBJC$_INSTANCE_VARIABLES_Person這個(gè)結(jié)構(gòu)體,我們查看一下這個(gè)結(jié)構(gòu)體的結(jié)構(gòu):
static struct /*_ivar_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count;
    struct _ivar_t ivar_list[2];
} _OBJC_$_INSTANCE_VARIABLES_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_ivar_t),
    2,
    {{(unsigned long int *)&OBJC_IVAR_$_Person$_age, "_age", "i", 2, 4},
     {(unsigned long int *)&OBJC_IVAR_$_Person$_no, "_no", "i", 2, 4}}
};

可以看到它有兩個(gè)成員變量,其中一個(gè)是_age,類(lèi)型是int,大小是4字節(jié),還有一個(gè)成員變量是_no,類(lèi)型是int,大小是4字節(jié)。

  • properties是屬性,可以猜測(cè)里面存儲(chǔ)的是屬性信息。我們看到其對(duì)應(yīng)的結(jié)構(gòu)體是_PROP_LIST_Person,查看一下其實(shí)現(xiàn):
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_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"no","Ti,N,V_no"}}
};

我們可以看到有一個(gè)屬性,屬性名是no。

元類(lèi)對(duì)象

我們找到class_t類(lèi)型的結(jié)構(gòu)體OBJC_METACLASS$_Person,這個(gè)結(jié)構(gòu)體就是元類(lèi)對(duì)象的實(shí)現(xiàn):

struct _class_t OBJC_METACLASS_$_Person __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_$_Person,

我們找到OBJC_METACLASS_RO$_Person這個(gè)結(jié)構(gòu)體

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

我們通過(guò)這個(gè)名稱(chēng)可以看出這是為初始化元類(lèi)對(duì)象的。
還是貼一下_class_ro_t的結(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;
};

對(duì)比來(lái)看,OBJC_METACLASS_RO_$_Person這個(gè)結(jié)構(gòu)體的初始化就要簡(jiǎn)單很多。

  • name就是類(lèi)名,是"Person"。
  • baseMethods存放的是方法,其是用OBJC$_CLASS_METHODS_Person這個(gè)結(jié)構(gòu)體初始化的,我們查看一下這個(gè)結(jié)構(gòu)體的結(jié)構(gòu):
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_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"personClassMethod", "v16@0:8", (void *)_C_Person_personClassMethod}}
};

類(lèi)方法只有一個(gè),方法名是“personClassMethod”。
其他的如baseProtocols,ivars,properties這些都是空的。
這也就證實(shí)了元類(lèi)對(duì)象中只有isa指針,superclass指針,還有類(lèi)方法。

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

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