美文网首页
OC底层原理06 - 类结构探索(1)

OC底层原理06 - 类结构探索(1)

作者: 卡布奇诺_95d2 | 来源:发表于2020-09-25 14:17 被阅读0次

    类是什么?

    类:指向objc_class结构体的指针

    typedef struct objc_class *Class;
    
    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
    }
    

    对象:指向objc_object结构体的指针

    typedef struct objc_object *id;
    
    struct objc_object {
    private:
        isa_t isa;
    }
    

    从上面源码中可以得到,objc_class继承于objc_object。换句话说,objc_class也是一个对象,后面称为类对象

    objc_class具有以下几个特点:

    • 并且它的第一个属性(即isa)是由objc_object继承而来。
    • superclass:这仍然是一个指向objc_class结构体的指针,指向父类。
    • cache:该结构体用来缓存方法。
    • bits:该结构体用来存储更加详细的类信息。

    总结:也是一种对象,在里面定义了一些属性和方法

    “万物皆对象”可能是由此而来的。

    isa指针

    objc_class结构体中,第一个成员就是isa,在之前的文章中,已经描述了如何将对象的isa与类关联起来,可是为什么在类的结构体里面又有isa呢?这个isa又指向什么呢?
    说到isa指针,不得不拿出isa走位图,该图清晰的描述了isa的指向

    isa走位图.png

    元类

    在分析isa走位图之前,先引入一个概念元类

    1. 元类系统给的,其定义和创建都是由编译器完成。
    2. 元类类对象,每一个类都有一个独一无二的元类存储类方法的相关信息。
    3. 元类本身是没有名称的,由于与类有关联,所以同类名一样的名称。
    4. 继承链中处于顶端元类,称为根元类。即所有NSObject子类元类都会以NSObject的元类为它们的根元类

    流程图分析

    从图中,可以得到以下信息:

    1. 对象的isa指向类;
    2. 类的isa指向元类;
    3. 元类的isa指向根元类;
    4. 根元类的isa指向自己;

    验证

    准备工作:

    1. 定义一个LGPerson类,使其继承于NSObject;
    2. 定义一个LGStudent类,使其继承于LGPerson;
    3. 最后在main函数中定义LGPerson对象和LGStudent对象。
    //定义一个LGPerson类,继承于NSObject
    @interface LGPerson : NSObject
    
    @property(nonatomic, strong) NSString* name;
    
    - (void)sayInstanceMethod;
    
    + (void)sayClassMethod;
    
    @end
    
    @implementation LGPerson
    
    - (void)sayInstanceMethod{
        NSLog(@"%s", __func__);
    }
    
    + (void)sayClassMethod{
        NSLog(@"%s", __func__);
    }
    
    @end
    
    //定义一个LGStudent类,继承于LGPerson
    @interface LGStudent : LGPerson
    
    @end
    
    @implementation LGStudent
    
    @end
    
    //在main.cpp中定义两个对象
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            LGPerson *person = [LGPerson alloc];
            LGStudent* student = [LGStudent alloc];
            NSLog(@"Hello, World!  %@, %@",person, student);
        }
        return 0;
    }
    

    在NSLog处打断点,让程序暂停在此处。接下来开始使用lldb进行调试。

    • 获取person对象的地址
    (lldb) p/x person
    (LGPerson *) $0 = 0x00000001006ebc90
    
    • 读取person对象所处地址的内存
    (lldb) x/4gx $0
    0x1006ebc90: 0x001d800100008255 0x0000000000000000
    0x1006ebca0: 0x697263534b575b2d 0x67617373654d7470
    
    • 读取isa中的类信息
      由之前对象的isa与类关联的分析中知道,person对象内存中第一个元素是isa。并且0x001d800100008255bit3 - bit35是类信息。
      因此可以通过掩码的方式解析出person对象的isa所指向的类的首地址。具体分析可查看之前文章。
    (lldb) po 0x001d800100008255 & 0x00007ffffffffff8ULL
    LGPerson
    
    (lldb) p 0x001d800100008255 & 0x00007ffffffffff8ULL
    (unsigned long long) $2 = 0x0000000100008250
    

    此时得到的LGPerson是类还是元类呢?通过runtime接口打印一下即可。

    //获取类的地址
    (lldb) p/x person.class
    (Class) $12 = 0x0000000100008250 LGPerson
    
    //获取元类的地址
    (lldb) p/x objc_getMetaClass("LGPerson")
    (Class) $13 = 0x0000000100008228
    

    此时可以知道。0x0000000100008250就是person对象的类。

    得到重要线索1:对象的isa指向类,此处person对象的isa指向LGPerson类

    既然说类也是一个对象,那我们就读取一下类首地址处的内存,看看有什么收获?

    • 读取LGPerson类首地址处的内存
    (lldb) x/4gx 0x0000000100008250
    0x100008250: 0x0000000100008228 0x000000010034c140
    0x100008260: 0x0000000100346430 0x0000801c00000000
    

    根据类的定义,第一个元素仍然是isa,那它会指向哪里?

    • 获取类的isa所包含的类信息
    (lldb) po 0x0000000100008228 & 0x00007ffffffffff8ULL
    LGPerson
    
    (lldb) p/x 0x0000000100008228 & 0x00007ffffffffff8ULL
    (unsigned long long) $3 = 0x0000000100008228
    

    得到重要线索2:类的isa指向元类,此处LGPerson类的isa指向LGPerson元类。

    • 接下来我们继续读取元类的isa所指向的内存,操作步骤之前相似,这里不重复描述。
    //读取该地址(LGPerson的isa指向的类首地址)下在的内存
    (lldb) x/4gx 0x0000000100008228
    0x100008228: 0x000000010034c0f0 0x000000010034c0f0
    0x100008238: 0x0000000103a040a0 0x0003e03500000007
    
    //获取内存中isa指向的类信息
    (lldb) po 0x000000010034c0f0 & 0x00007ffffffffff8ULL
    NSObject
    
    //继续获取isa指向的内存地址
    (lldb) p 0x000000010034c0f0 & 0x00007ffffffffff8ULL
    (unsigned long long) $4 = 0x000000010034c0f0
    
    //读取isa指向的内存地址
    (lldb) x/4gx 0x000000010034c0f0
    0x10034c0f0: 0x000000010034c0f0 0x000000010034c140
    0x10034c100: 0x0000000103a044b0 0x0004e03100000007
    
    //获取内存中isa指向的类信息
    (lldb) po 0x000000010034c0f0 & 0x00007ffffffffff8ULL
    NSObject
    

    此时类信息已经变成NSObject,并且继续对NSObject读取isa指向时,发现一直指向的同一个NSObject(因为地址没有发生改变)。

    得到两条线索:
    1. LGPerson元类isa指向根元类,此处的根元类NSObject;
    2. 根元类isa指向根元类自己

    通过一步一步读取isa的指向,验证了isa走位图isa的走位

    总结:

    作为重点,这里再强调一遍isa的走位。
    1. 实例对象(Instance of Subclass)的isa指向类(class)
    2. 类对象(class)的isa指向元类(Meta class)
    3. 元类(Meta class)的isa指向根元类(Root metal class)
    4. 根元类(Root metal class)的isa指向它自己本身

    supperclass

    isa走位图里面还描述了一个superclass,表示继承关系,图中描述的是:

    1. 子类的类对象superclass指向的是它的父类的类对象,即子类继承父类
    2. 子类的元类对象superclass指向的是它的父类的元类对象,即子元类继承父元类
    3. 根元类对象superclass指向根类对象,即根元类继承根类
    4. 最终的superclass指向nil

    接下来也来验证一下:

    • 分别读取NSObject类NSObject元类LGPerson类LGPerson元类LGStudent类LGStudent元类地址,为后续比较做准备。
    //NSObject 类地址
    (lldb) p/x objc_getClass("NSObject")
    (Class) $1 = 0x000000010034c140 NSObject
    
    //NSObject 元类地址
    (lldb) p/x objc_getMetaClass("NSObject")
    (Class) $2 = 0x000000010034c0f0
    
    //LGPerson 类地址
    (lldb) p/x objc_getClass("LGPerson")
    (Class) $3 = 0x00000001000082e8 LGPerson
    
    //LGPerson 元类地址
    (lldb) p/x objc_getMetaClass("LGPerson")
    (Class) $4 = 0x00000001000082c0
    
    //LGStudent 类地址
    (lldb) p/x objc_getClass("LGStudent")
    (Class) $5 = 0x0000000100008338 LGStudent
    
    //LGStudent 元类地址
    (lldb) p/x objc_getMetaClass("LGStudent")
    (Class) $6 = 0x0000000100008310
    
    • 读取LGStudent 类supperclass
    (lldb) p/x LGStudent.superclass
    (Class) $7 = 0x00000001000082e8 LGPerson
    

    得出结论1:LGStudent 类supperclassLGPerson类

    • 读取LGPerson类supperclass
    (lldb) p/x LGPerson.superclass
    (Class) $8 = 0x000000010034c140 NSObject
    

    得出结论2:LGPerson 类supperclassNSObject类

    • 读取NSObject类supperclass
    (lldb) p/x NSObject.superclass
    (Class) $9 = nil
    

    得出结论3:NSObject 类supperclassnil

    • 读取LGStudent 元类supperclass
    (lldb) p/x ((LGStudent*)objc_getMetaClass("LGStudent")).superclass
    (Class) $10 = 0x00000001000082c0
    

    得出结论4:LGStudent 元类supperclassLGPerson 元类

    • 读取LGPerson 元类supperclass
    (lldb) p/x ((LGPerson*)objc_getMetaClass("LGPerson")).superclass
    (Class) $11 = 0x000000010034c0f0
    

    得出结论5:LGPerson 元类supperclassNSObject 元类

    • 读取NSObject 元类supperclass
    (lldb) p/x ((NSObject*)objc_getMetaClass("NSObject")).superclass
    (Class) $12 = 0x000000010034c140 NSObject
    

    得出结论6:NSObject 元类supperclassNSObject 类
    至此,已经完成supperclass走位图的验证。

    总结:
    1. 类(subClass)继承自父类(superClass)
    2. 父类(superClass)继承自根类(RootClass)
    3. 根类继承自nil根类可以理解为万物起源
    4. 子类的元类(metal SubClass)继承自父类的元类(metal SuperClass)
    5. 父类的元类(metal SuperClass)继承自根元类(Root metal Class)
    6. 根元类(Root metal Class)继承自根类(Root class)

    相关文章

      网友评论

          本文标题:OC底层原理06 - 类结构探索(1)

          本文链接:https://www.haomeiwen.com/subject/drwnyktx.html