类的结构分析

简介: 我们来分析类的结构: 先引入一个列子,(注意以下代码是在objc源码中调试的):例子说明: CJLPerson继承于NSObject, CJLTeacher继承于CJLPerson

总纲领: OC底层探寻


我们来分析类的结构: 先引入一个列子,(注意以下代码是在objc源码中调试的):

例子说明: CJLPerson继承于NSObject, CJLTeacher继承于CJLPerson


int main(int argc, const char * argv[]) {  
    @autoreleasepool {  
        //ISA_MASK  0x00007ffffffffff8ULL  
        CJLPerson *person = [CJLPerson alloc];  
        CJLTeacher *teacher = [CJLTeacher alloc];  
        NSLog(@"Hello, World! %@ - %@",person,teacher);    
    }  
    return 0;  
}


以下截图引用外部


微信图片_20220509102728.png

lldb调试过程 上


微信图片_20220509102736.png

lldb调试过程 中


微信图片_20220509102742.png

lldb调试过程 下


调试代码有点长, 但是最终我们所得到的结果, 就是非常经典的一幅图:


微信图片_20220509102746.png

类的继承以及类的isa指针总结图


类的结构分析


下面我们来一段objc_class的源码:


struct objc_class : objc_object {  
    // Class ISA;   //8字节
    Class superclass;   //Class是由objc_object定义的, 是一个指针, 8字节
    cache_t cache;             // formerly cache pointer and vtable  //结构体要具体分析里面的属性, 分析出来也是8字节
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags  
    class_rw_t *data() const {  
        return bits.data();  
    }  
    void setData(class_rw_t *newData) {  
        bits.setData(newData);  
    }  
    void setInfo(uint32_t set) {  
        ASSERT(isFuture()  ||  isRealized());  
        data()->setFlags(set);  
    }  
    void clearInfo(uint32_t clear) {  
        ASSERT(isFuture()  ||  isRealized());  
        data()->clearFlags(clear);  
    }  
    // set and clear must not overlap  
    void changeInfo(uint32_t set, uint32_t clear) {  
        ASSERT(isFuture()  ||  isRealized());  
        ASSERT((set & clear) == 0);  
        data()->changeFlags(set, clear);  
    }


从上面代码我们可以看到 objc_class 继承与 objc_object  而objc_object的源码为:


/// Represents an instance of a class.  
struct objc_object {  
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;  
};


我们可以看到objc_object是一个结构体, 并且有一个isa属性, 而objc_class 继承于 objc_objt , 所以objc_class也有isa属性.

我们可以总结一下:


  1. 所有的对象 , 类 都有isa属性.
  2. objc_class继承于objc_object, 可以总结为万物皆对象.


要对类的结构分析, 这里要引入一个概念--内存偏移


//数组指针  
    int c[4] = {1, 2, 3, 4};  
    int *d = c;  
    NSLog(@"%p -- %p - %p", &c, &c[0], &c[1]);  
    NSLog(@"%p -- %p - %p", d, d+1, d+2);


打印结果:


微信图片_20220509102751.png

打印结果.png


我们可以通过内存偏移来分析objc_class, 接刚开始objc_class的源码, 我们来分析cache:


struct cache_t {  
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED  
    explicit_atomic<struct bucket_t *> _buckets;  //*指针, 占8字节
    explicit_atomic<mask_t> _mask;  //mask_t 是unsigned int 的别名, 占4字节
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16  
    explicit_atomic<uintptr_t> _maskAndBuckets;  //uintptr_t 是一个指针, 8字节
    mask_t _mask_unused;  //mask_t 是unsigned int 的别名, 占4字节
    // How much the mask is shifted by.  
    static constexpr uintptr_t maskShift = 48;   //static 静态类型不存储在结构体中, 所以不算是结构体的内存
    // Additional bits after the mask which must be zero. msgSend  
    // takes advantage of these additional bits to construct the value  
    // `mask << 4` from `_maskAndBuckets` in a single instruction.  
    static constexpr uintptr_t maskZeroBits = 4;  
    // The largest mask value we can store.  
    static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;  
    // The mask applied to `_maskAndBuckets` to retrieve the buckets pointer.  
    static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1;  
    // Ensure we have enough bits for the buckets pointer.  
    static_assert(bucketsMask >= MACH_VM_MAX_ADDRESS, "Bucket field doesn't have enough bits for arbitrary pointers.");  
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4  
    // _maskAndBuckets stores the mask shift in the low 4 bits, and  
    // the buckets pointer in the remainder of the value. The mask  
    // shift is the value where (0xffff >> shift) produces the correct  
    // mask. This is equal to 16 - log2(cache_size).  
    explicit_atomic<uintptr_t> _maskAndBuckets;  
    mask_t _mask_unused;  
    static constexpr uintptr_t maskBits = 4;  
    static constexpr uintptr_t maskMask = (1 << maskBits) - 1;  
    static constexpr uintptr_t bucketsMask = ~maskMask;  
#else  
#error Unknown cache mask storage type.  
#endif  
#if __LP64__  
    uint16_t _flags;  //uint16_t 是unsigned short的别名 占2个字节
#endif  
    uint16_t _occupied;  //uint16_t 是unsigned short的别名 占2个字节


通过上面的分析, 我们可以分析出不管走哪个路径, cache_t 一共占 8 + 4 + 2 + 2 = 16字节, 所以要想知道bits中的内容, 只需通过类的首地址, 然后平移32字节就可以得到了:


微信图片_20220509102758.png

打印结果


总结


  1. 通过{}定义的成员变量, 会存储在类的bits属性中, 路径为bits --> data() --> ro() --> ivars获取成员变量列表, 除了包括成员变量, 还包括属性定义的成员变量.
  2. 通过@property定义的属性, 也会存储在类的bits属性中, 路径为bits --> data() --> properties() --> list获取属性列表, 其中只包含属性.
  3. 类的实例方法存储在类的bits属性中,路径为bits --> methods() --> list获取实例方法列表, 其中除了包括实例方法, 还包括属性的set方法和get方法.
  4. 类的类方法存储在元类的bits属性中, 路径为元类的bits --> methods() --> list获取类方法列表.




目录
相关文章
|
9月前
|
算法 C++ 容器
关系类算法函数
关系类算法函数
|
12月前
|
存储 编译器 C语言
C++ 基础篇之类 & 对象的关系
C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。
|
编译器 C++
C++把类的设计看成类型设计
C++把类的设计看成类型设计
89 0
类的结构分析 下
1.class_getInstanceMethod : 获取实例方法, 如果指定类或其父类不包含带有指定选择的实例方法, 则为NULL. 2.class_getClassMethod : 获取类方法, 如果指定类或其父类不包含具有指定的类方法, 则为NULL. 3.class_getMethodImplementation : 获取方法的具体实现, 如果未查找到, 则进行消息转发.
97 0
类的结构分析 下
|
存储 开发者 Python
对象的结构|学习笔记
快速学习 对象的结构
101 0
|
C#
C#中的结构与类
C#中的结构与类
123 0
|
知识图谱
欧几里德结构数据与 非欧几里德结构数据
欧几里德结构数据与 非欧几里德结构数据
448 0
欧几里德结构数据与 非欧几里德结构数据
|
Java 程序员
系统定义的异常类及其层次
Java程序中,由于程序员的疏忽和环境因素的变化,会经常出现异常情况,为了保证程序的正常运行,Java语言专门提供了异常处理机制; Java针对各种常见的异常定义了相应的异常类,并建立了异常类体系。
101 0
系统定义的异常类及其层次
|
Java 开发者
反射获取类结构信息|学习笔记
快速学习反射获取类结构信息
108 0
反射获取类结构信息|学习笔记
|
传感器 编解码 算法