• Runtime的本质(二)objc_class结构


    这篇文章,我们主要来介绍一下objc_class结构的内容与含义。

    我们知道Class的类型是objc_class类型
    typedef struct objc_class *Class;
    点进去objc_class可以看到部分定义:

    在这里插入图片描述

    objc_class继承objc_object
    objc_object的部分定义:

    在这里插入图片描述

    简化可归结objc_class的结构为:

    在这里插入图片描述

    可以看出:
    Class里面有isa、superclass指针,方法列表、属性列表、协议列表以及成员变量列表以及其他信息。
    其中,方法列表包含了自己的方法列表以及分类的方法列表,且分类方法列表在原类方法列表前面。

    通过操作objc_class里面的bits与上一个定义好的数值,就可以找到class_rw_t里面的内容。这种做法我们在上一篇文章中已经讲解过,用到的是位运算操作,这里不再做叙述。

    其中:
    class_rw_t中的rw是read和write的意思,也就是class_rw_t里面的内容是可读可写的。
    class_ro_t中的ro是read和only read的意思,也就是class_ro_t里面的内容是只读的。

    通过iOS大神MJ老师写的文件MJClassInfo.h,我们可以窥探class的具体内容。

    导入文件,注意,Add to targets不要选中
    在这里插入图片描述

    YZPerson *person = [[YZPerson alloc] init];
    mj_objc_class *cls = (__bridge struct mj_objc_class *)([YZPerson class]);
    class_rw_t *data = cls->data();
    

    img

    好强大的工具,给MJ点赞

    在class_rw_t存储的方法列表、属性列表、协议列表都是二维数组。

    以方法列表为例:
    method_array_t里面是method_list_t;method_list_t里面是结构体method_t

    struct method_t {
        SEL name;
        const char *types;
        IMP imp;
    
        struct SortBySELAddress :
            public std::binary_function<const method_t&,
                                        const method_t&, bool>
        {
            bool operator() (const method_t& lhs,
                             const method_t& rhs)
            { return lhs.name < rhs.name; }
        };
    };
    

    在源码中可以看出:

    在这里插入图片描述

    在这里插入图片描述

    img

    而class_ro_t里面也有方法列表

    在这里插入图片描述

    参考源码,可以得出:
    原始的class_ro_t中的方法列表与分类中的方法列表结合,放到class_rw_t的方法列表中。

    更多学习:
    Runtime源代码解读4(方法列表)
    这是个大佬

    method_t分析

    method_t是对方法/函数的封装。
    通过查看源码,可以看到method_t结构的组成:

    struct method_t {
        SEL name;//函数名
        const char *types;//编码(返回值类型、参数类型)
        IMP imp;//指向函数的指针(函数地址)
    };
    

    其中,IMP代表函数的具体实现,imp是指向函数地址的指针,通过IMP可以真正的找到函数的入口。

    SEL代表方法/函数名,一般叫做选择器,底层结构跟char *类似。
    可以通过@selector()和sel_registerName()获得
    可以通过sel_getName()和NSStringFromSelector()转成字符串
    不同类中相同名字的方法,所对应的方法选择器是相同的

    types是一个包含了函数的返回值类型、参数类型的编码字符串
    其属于Type Encoding类型编码
    iOS提供了一个叫做@encode的指令,可以将具体的类型表示成字符串编码。
    对应含义可以在官方文档查看,这里仅列出一部分:
    在这里插入图片描述

    我们需要了解的是:
    可以通过method_t里面的types获取到函数的返回值类型以及参数类型。

    方法缓存

    在objc_class里面有一个cache_t类型的cache,用散列表来缓存曾经调用过的方法,可以提高方法的查找速度。
    其中,cache_t的组成为:

    struct cache_t {
        struct bucket_t *_buckets;//散列表
        mask_t _mask;//散列长度-1
        mask_t _occupied;//已经缓存的方法数量
    }
    
    struct bucket_t {
        cache_key_t _key;//SEL作为key
        IMP _imp;//函数的内存地址
    };
    

    可以看出,用方法名作为key,找函数内存地址,形成一个散列表。

    举个具体例子分析:

    YZPerson *person = [[YZPerson alloc] init];
    [person run];
    

    方法缓存过程:

    第一次调用某个对象方法的时候,会去该类的类对象cache中通过key-value方法查找,key是方法名,查找对应的IMP。
    如果查找不到,会通过isa指针去其类对象里面查找该方法,有的话就通过散列表缓存到cache中并且调用该方法。
    如果该类对象里面没有该方法,则去其父类里面找,也是先去缓存列表中找,找到就放入到自己的缓存中并调用。缓存中没有则去方法列表中找,找到会通过散列表缓存到自己cache中并且调用,没找到会继续去父类中找,直到找不到。第二次调用,去cache中查找,有,直接调用,没有继续按上面的方法做。

  • 相关阅读:
    大话设计模式总结(28种设计模式定义+简单理解)
    Dbank网盘下载地址提取ASP
    C#特性详解
    wcf大文件传输解决之道(1)
    WCF完美搭建android平台服务之一
    堆栈和委托堆的区别(c#)一
    wcf完美搭建android平台服务之三
    liunx简单命令集合
    WCF完美搭建android平台服务之二
    WCF中常用的binding方式比较
  • 原文地址:https://www.cnblogs.com/r360/p/15812447.html
Copyright © 2020-2023  润新知