美文网首页
类的原理(上)

类的原理(上)

作者: KG丿夏沫 | 来源:发表于2021-07-02 13:30 被阅读0次

    对于一个iOS开发者来说对象是最熟悉不过的,因为我们开发的时候时刻都是在操作各种对象,而且都知道对象是通过了类的初始化创建出来的,那么问题来了,我们都知道类是通过继承实现方法以及属性和协议的传递,那么底层原理是怎么实现的呢?那么带着对未知的好奇心和求知欲我们一起探索类的原理,今天主要探索isa

    知识补充:isa

    我们都知道isa是个指针,那么他是一个什么样的指针呢?我们先创建一个NSObject对象,然后按住command键+鼠标右键点击,我们就可以进入类定义的地方,我们能够看到如下代码:

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

    NSObject中有一个成员变量isa,而且是Class类型,然后我们继续看这个Class类型的时候会发现进不去了,我们看不到了。

    161623895163_.pic.jpg

    引用某人的口头禅好烦,那么接下来就上大招了,objc源码工程,我们从源码工程中进行搜索Class

    171623895803_.pic.jpg

    貌似有点多啊,但是经过我的不断查找(加上猜测),所以最终锁定这么一行代码:

    typedef struct objc_class *Class;
    

    瞬间明白为啥经常要说isa指针了,这不就是结构体指针嘛。然后再次引出来一个问题,我们都知道类继承就是isa幕后操手,那么继续看下这个objc_class结构体的构成,欲知后事如何,请听下回分析。

    191623896379_.pic.jpg

    知识补充:objc_class

    对于结构体我们都不陌生,毕竟开发中也会经常遇到,那么这节我们一起看下objc_class结构体,继续在objc源码工程中进行全局搜索struct objc_class,(⊙o⊙)…貌似和我想的不一样,咋这么多:

    image.png

    机智如我,经过这么一观察发现,一个是objc-runtime-new.h、一个是objc-runtime-old.h,那么直接放弃objc-runtime-old.h因为我们现在用的是新版OBJC2,然后再去看下runtime.hobjc_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;
    

    结构体最后有一个标记OBJC2_UNAVAILABLE意思就是告诉我们在OBJC2中不让用,那么我们就只需要去看objc-runtime-new.h中的声明了,愉快的打开objc-runtime-new.h,然后......

    241623896963_.pic.jpg

    这个结构体出乎意料了,好长,然后大概瞅了那么一圈,嘿,貌似不长,都是函数我们需要的是了解它的成员有那些,所以经过简化后,结果如下:

    struct objc_class : objc_object {
        // Class ISA; 这块系统注释的原因是告诉我们,这个结构体还有一个影藏的成员```ISA```
        Class superclass; //父类
        cache_t cache;             // 用来缓存指针和虚函数表
        class_data_bits_t bits;    // 存储类的信息的,包括成员列表、属性列表、方法列表、协议列表等等
    }
    

    然后到这块基本上已经了解了isa指针的本质objc_class结构体,那么顺带我们瞄一眼objc_object,发现里面就一个成员isa_t isa;,至于isa_t的结构体的介绍,在我的对象的本质这篇博客中有详细介绍,在这里就不多做解释了,感兴趣可以去看下。

    到这里前面的知识补充就完成了,也就是开胃小菜吃完了,那么开始今天的重点,类的继承链也即是我们经常能够看到的isa走位以及通过源码搭配lldb去分析查看类的结构。

    类的superClass走位

    1、首先我们看下类的继承链,我们打开Xcode->command+shift+N->macOS->Command Line Tool然后起个霸气的名字。

    2、然后创建一个基于NSObject的类KGPerson,然后添加如下几个属性、成员变量、对象方法以及类方法:

    image.png

    3、然后基于KGPerson创建一个KGTeacher类。

    4、然后在main.m中引入KGPersonKGTeacher的头文件。

    5、声明并实现一个函数void kgTestSuperClass(void),然后在main函数中调用,具体代码如下:

    image.png

    然后我们运行程序,最后打印结果如下:

    image.png

    6、到此那么我们可以得出如下一个集成链的isa走位图:

    未命名.png

    类的isa走位

    1、在基于上一过程中创建的项目以及代码基础上我们在main函数中添加一个函数,如下:

    image.png

    2、运行项目后得到的结果如下图所示:

    image.png

    3、分析输出的结果,我们可以得到如下的isa走位图:

    未命名 (1).png

    4、然后我们再看下superClassisa走位图,继续修改main.m代码如下:

    image.png

    5、运行结果如下:

    image.png

    6、通过打印结果,我们再次得到这样一个元类的superClass走位图:

    未命名 (2).png

    7、我们继续探索,元类的父类的isa是如何走的呢?下面继续修改main.m中的代码,如下:

    image.png

    8、代码运行结果如下:

    image.png

    9、最后结合之前的superClass走位图以及isa走位图,我们最后能够证明得到这个全球通用的图:

    isa流程图.png

    10、分析完成类的继承链以及元类的继承链后,回到开始的时候,我们的好奇心提出的疑问,下面我们一起通过源码搭配lldb去剖析类的原理。

    类的属性

    1、我们先前在KGPerson中创建了两个属性、一个成员变量、一个类方法、一个对象方法,下面我们先去看下类的属性是存储在哪里的。

    2、我们前面以及探索了isa的结构,那么我们通过它的结构去查看存储的类信息,既然要看属性,那么我们必定是需要访问bits了它在类结构体中定义是这样的:class_data_bits_t bits;

    3、我们需要访问的数据存储在bits里面,但是我们只能得到类的地址,也就是isa的地址,那么我们怎么去访问后面的元素呢?请看下面一个简单的示例:

    image.png

    然后输出如下:

    image.png

    4、从上面小测试我们就可以看到,我们可以通过指针地址的偏移去访问地址指向的存储空间中的值,那么我们接下来看下,我们想要访问bits中的内容,我们需要将指针移动多少位?我们前面看了objc_classbits前面有三个成员ISAsuperclasscache,然后isa是一个结构体指针占8位我们都知道,然后superclass是个Class类型,我们查看isa的时候就发现了Class也是个结构体指针,那就是说superclasss也是占8位,然后去看cache时发现这是个结构体,而且结构体中有两个成员,一个是_bucketsAndMaybeMask它的大小看uintptr_t的大小,uintptr_t又是一个无符号长整型数据,它在32位系统下是4字节,在64位系统下是8字节,我们现在都是在64位系统下进行的,所以它是8字节;另一个是联合体,然后联合体里面又是一个结构体和_originalPreoptCache,我们都知道联合体成员之间是互斥的,所以我们不看结构体,直接看_originalPreoptCache的大小,explicit_atomic<preopt_cache_t *> _originalPreoptCache;我们可以看到它的类型收到preopt_cache_t *的影响,然后preopt_cache_t *是个结构体指针,那么它的大小是8字节,所以得到如下结论:

    image.png

    5、所以我们如果想要访问bits需要进行地址偏移32位,地址是采用十六进制所以我们需要在原地址基础上加上0x20才可以访问到bits,那么我们先通过lldb测试下是否正确。

    [图片上传失败...(image-c00025-1624257325729)]

    6、从图上可以看出我们的猜测是完全正确的,确实偏移32位后能够访问到bits。然后我们继续分析class_data_bits_t结构体,发现里面有个data()方法,然后返回值是个class_rw_t*的指针,具体代码如下:

    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    

    7、那么我们去看下这个class_rw_t结构体,发现好多方法和成员,但是滑动到最后的时候看到了几个熟悉的单词如下:

    image.png

    8、那么我们能否猜测,这几个方法就是读取属性、方法、协议方法的呢?带着好奇,我们先去看下properties,具体操作是在上次的基础上进行,如下:

    202106191335_2_.gif

    9、通过以上步骤我们验证了一个类的属性存储在bits中,我们可以通过以下流程来获取类的属性:

    未命名.png

    10、到这里属性在类中存储的位置已经找到了,那么接下来我们再去分析下成员变量存储的位置。

    类的对象方法

    1、上面两节我们探索了类存储属性以及成员变量的位置,那么这节开始探索类的对象方法的存储位置,前面介绍属性存储位置的时候,我们是通过properties()来获取的,同时我们找的时候也看到了有个methods()函数,那么我们可以做个大胆的猜测,是否这个函数就是获取方法列表的呢?下面我们通过lldb在源码工程中进行调试验证一下。

    [图片上传失败...(image-4b7e-1624257459334)]

    2、通过上述验证我们可以确定properties()中确实存储的方法,具体存储了哪些呢?下面我们继续读取:

    image.png 954C82732570545214DD4E07059C38D4.gif

    为啥不是我想象的那种样,出现我们熟悉的方法名呢?瞬间感觉好像在哪忽略了什么,于是我们再次回到class_rw_t结构体,然后顺着methods找到了method_array_t然后继续查找到了method_t结构体,看到如下内容:

    image.png

    发现我们需要的东西在big这个结构体内,然后我们继续往下查找,发现如下方法:

    image.png

    3、我们通过以上的查找得到了查找流程,那么我们继续去验证一下,通过lldb调试后,结果如下:

    [图片上传失败...(image-729895-1624257474342)]

    如上图我们能够看到namehomeTownsettergetter方法,但是没有看到我们的类方法和对象方法,我猜测是否是因为没有实现导致呢?然后我们去实现方法,再次运行后发现只有对象方法没有类方法,结果如下:

    image.png

    4、通过打印发现对象方法是有了,但是没有类方法,那么类方法存储在哪里呢?请听下回分析。

    总结

    就俩字欧耶

    B583247F0E2500F680D1131BA314413C.gif

    相关文章

      网友评论

          本文标题:类的原理(上)

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