zoukankan      html  css  js  c++  java
  • OC 底层探索 04、类的结构分析 & isa & bits

    本文内容主要对isa指向流程 和 类的结构以及类中 bits 进行探索。

    一、类与 isa

    运行 objc源码工程,main.m 文件中断点打在 objc2,读取对象 objc2 的内存如下:

    图中,我们发现两个不同的 地址,他们的值都是 MyPerson。这是为何呢?--> 元类

    我们继续读取内存,如下图:

    结合以上内容,我们可以得出 objc2 --> MyPerson --> MyPerson --> NSObject 

    即:isa 对象 --> --> 元类 --> NSObject 类 

    上图中,通过 NSObject.class 看到: NSObject --> NSObject

    有2个不同地址的 NSObject ,它又是为何呢?内存中会存在多个 NSObject 对象吗?

    不会!类对象在内存中存在一份!!!

    我们以 MyPerson 简单验证类对象在内存中只有一个

     

    alloc 多次,class 地址相同,只有一个。

    isa 指向流程 

    从上面第二幅图中可以观察到(注意黄线标识部分),NSObject 类,内存读到  0x00000001003ee0f0 后,开始指向自己。

    --> 实例对象 --> 类 --> 元类 --> NSObject 根元类 --> NSObject 根元类 

    isa 指向图:

    小小注意点:

    实例对象之间时不存在关系的,所谓的父子继承是针对类而言。

    NSObject(Root class) 的父类是nil,NSObject 的元类是根元类(root),根元类的父类是 NSObject,根元类的元类是自身

    OC - NSObject 万物之源!!!

    二、类与对象

    1、objc_object 和 objc_class

    通过源码查找:

    objc_object - 对象 : --> OC 的 NSObject 底层 --> objc_object 结构体

    objc_class - 类 :--> 继承自 objc_object

    所有对象、类、元类都有 isa,它们是来源于继承的 objc_object 里面的 isa. 

    对象 --> objc_object

    类    --> objc_class

    而 objc_class  --继承自-->  objc_object --> class 也是个对象 --> 万物皆对象

    --> 引申:类是什么?类是元类的实例对象

    问题:objc_object 和 对象的关系

    从上面我们可知,所有的对象本质上都是基于 objc_object 为模板创建的。

    OC 的底层其实就是 C/C++,OC 相当于一层封装。以结构体 objc_object 为模板,创建了所有 OC对象,进而提供给我们使用。苹果的大特质就是封装啊。

    本质 - 万物皆 objc_object !!!

    objc_object / objc_class / object / NSObject / isa 关系图:

    三、类的结构分析

    通过源码查看结构:

    在继续探索类之前我们要先简单介绍下指针和内存偏移。

    1、扩展 - 指针和内存偏移 

    1、普通指针

     

    指针地址不同,浅拷贝 也称值拷贝。

    2、对象指针

    objc1 objc2 指针 指向 [MyPerson alloc] 开辟的空间-内存地址;

    &objc1 &objc2 指针的地址 --> 二级指针

    3、数组指针

    如上图,*p 指针指向arr,即 arr 首地址。通过指针偏移,可一次获取到 arr 中数据。

    2、类 的结构和内容 

    isa:继承 objc_object 的isa ,8字节

    superclass:Class 类型 指针,8字节

    catch_t :结构体,结构体大小的计算我们在内存对齐中有介绍,它是内部全部属性的大小,且要遵循内存对齐原则。

    catch_t 的大小计算:

    if -->

       bucket_t: struct bucket_t * 指针类型 8字节;

       mask_t: uint32_t  4 字节;

    elif -->

        uintptr_t:指针类型 8字节;

        mask_t:uint32_t   4 字节;

    其他 -->

        __flag:unsigned short uint16_t  2 字节

      _occupied:unsigned short uint16_t  2 字节

    8 + 4 + 2 + 2 = 16 字节  

    3、类中的信息 - bits

    1、找到类信息

    1、直接拿到类的首地址方式(lldb):bits 前面有三个变量 8+8+16 = 32.

      p/x MyPerson.class

      x/4gx MyPerson.class

    2、计算 bits 地址 --> 首地址平移 32 字节

      16进制,32 即进位 2 --> 16进制地址 第二位 + 2

    3、通过源码 1250行 (见下图),得知 bits 中有个 ‘class_rw_t  *data’ 的data。

    拿到 data:$2->data().

    读取到data:p *$4

      

    查看 bits 流程 --> 类的首地址 + 32 --> bits 地址 --> (class_data_bits_t *)bits地址 --> bits.data() --> (class_rw_t )data{} 

    如上图,我们看到了 $5 中的一些信息,但是我们的属性方法列表在哪里呢?

    2、bits

    MyPerson 代码(这里为了便于区分后来将定义的 name 改为 propName了)

    @interface MyPerson : NSObject {
       
        NSString *instName;
    }
    
    @property (nonatomic, copy) NSString *propName;
    
    - (void)funcObjcTest;
    + (void)funcClassTest;
    @end
    
    @implementation MyPerson
    - (void)funcTest {
        
    }
    + (void)funcClassTest {
      
    }
    @end

    class_rw_t

    点击进去,比较长,一直滑到下面,如图:

     

    由源码我们可知,class_rw_t 中有 方法属性协议 列表 。

    1、property_list 属性列表

    根据源码,我们这里进行 property() 的查询:

     

    propety_list 中:只有一个 “name” 属性存在。

    2、method_list 方法列表 - 实例方法

    p $21.get(4) error 数组越界。 

    method_list 中:propName 的 setter/getter 方法实例方法 funcTest。setter/getter 方法由系统自动帮我们生成。

             .cxx_destruct:OC 封装于C++底层,默认添加。

    从上可知,我们的实例变量和类方法并不在 property() 和 method() 中,继续探索。

    3、class_ro_t 实例变量

    结构体 class_rw_t 中 set_ro --> class_ro_t

     

    class_ro_t

    通过源码,可以猜测实例变量存在 ivars 中,我们做如下验证作操作:

    如上,我们可知,实例变量和属性变量的_propName 都在 ivars 中。属性变量、成员变量简介.

    我们继续寻找类方法在哪里。 

    通过文章上面的isa 探索部分,我们知道 实例指向类,类指向元类;实例方法在类中,那么类方法是否在元类中呢?

    4、验证类方法位置

    如上,我们通过 MyPerson.class 的 isa,拿到元类,然后进行地址平移 32 字节,之后操作如图,果然,类方法在其中找到。

    同时,这里也可验证我们上面所探究的 isa 流向。

    总结:

    isa

      实例    --> 

      类       -->  元类

      元类    -->  根元类

      根元类 -->  根元类

    方法:

      实例方法 --> 类中

      类方法    --> 元类中

    变量:

      属性变量                --> propety_list

      成员变量/_属性变量 --> ivars

  • 相关阅读:
    请求浏览器使用chrome查看http请求
    输入数据问题一百一十二:C语言合法标识符(2)
    方法调用代理代码改进
    串字符串问题一百一十三:Palindromes _easy version
    删除系统Win7系统盘越来越小,系统盘清理技巧
    配置编译linux下QT程序编译时的错误:QMAKESPEC has not been set, so configuration cannot be deduced.
    整数实例java处理大整数
    触发器课程SQL Server 知识梳理九 触发器的使用
    输出整数回溯法解决素数环
    类注解Spring注解自动注入Bean
  • 原文地址:https://www.cnblogs.com/zhangzhang-y/p/13664238.html
Copyright © 2011-2022 走看看