OC Runtime小记 (一) runtime 数据结构

OC Runtime小记 (一) runtime 数据结构

前言

我们作为一个iOS的开发,毕竟是用OC搬砖的,抱着苹果爸爸的大腿,不能总做些业务,UI等等(虽然现实如此 //摊手)。现在iOS未开源,但是我们还是要了解一下底层原理的,runtime作为OC的重要API之一,还是好好的学习下。

runtime 在现在的应用场景大概有这些:热修复,动态获取方法,方法交换,KVC,动态添加方法,自动埋点,统计等等。

runtime 原理探究

OC作为一门动态语言,主要体现在他将许多需要在静态语言编译链接时候要做的事情,通通放到了运行时去做,不像C语言一样,在全部静态链接完毕后。

OC作为运行时分为两种方式:Morden runtimelegacy runtime,现在的运行时与遗留的运行时区别在于:遗留的运行时在改变一个类的结构时,你必须继承它并重新编译。而现在的运行时可以直接编译。

静态类型编程语言(比如C++)的函数调用和OC的方法调用(消息发送)的区别在于前者在编译期就确定了(函数的地址),而后者是运行时动态确定.

Runtime 的数据结构

runtime 代码链接

基础数据结构

1 id object IMP
typedef struct objc_class *Class;

typedef struct objc_object {
    Class isa;
} *id;

typedef struct objc_selector     *SEL;    
typedef id             (*IMP)(id, SEL, ...); 

这里可以看出 Class 是指向objc_class 数据结构的指针,而id则为指向objc_object数据结构的指针,这里id就是对象,class就是类,IMP 是一个函数指针,SEL在一些看到博客中记录为是一个char类型的指针,这边还未验证,等后面验证了再单独写一篇。

2 objc_class
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;         //指向元类对象

    #if !__OBJC2__
    //父类
    Class super_class                                        OBJC2_UNAVAILABLE;
    //类名
    const char *name                                         OBJC2_UNAVAILABLE;
    //类的版本信息 
    long version                                             OBJC2_UNAVAILABLE;
    //类信息,运行期间的一些标志位
    long info                                                OBJC2_UNAVAILABLE;
    //此类的实例变量的大小
    long instance_size                                       OBJC2_UNAVAILABLE;
    //此类的成员变量链表
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    //此类的方法定义的链表
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    //方法缓存的链表,用于优化method_list 的查找
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    //协议链表
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
    #endif

} OBJC2_UNAVAILABLE;

objc_class中可以看到两者都含有一个 isa 指针,那么可以理解其实objc_class其实也是一个对象,是一个里面含有更多信息成员变量的对象,在具体区分的话可以这么理解:object_class 是一个类对象,object_objcet 是一个实例对象。

3 isa

objc_class 中的isa指针指向 metaClass 里面存放着(+)方法,static 类型的成员变量
objc_object 中的isa指针指向该类所属的类,其中存放着普通成员变量和(-)动态方法

4 super_class

super_class指向该类的根类 如果该类是根类则为nil

下图为:继承层次关系图
继承关系图

所有的metaclass中isa指针都是指向根metaclass,而根metaclass则指向自身。根metaclass是通过继承根类产生的,与根class结构体成员一致,不同的是根metaclass的isa指针指向自身。

5 objc_ivar_list objc_ivar

objc_ivar_list结构体存储着objc_ivar数组列表,而objc_ivar结构体存储了类的单个成员变量的信息。

typedef struct objc_ivar *Ivar;

struct objc_ivar_list {
int ivar_count;                        //变量个数
#ifdef __alpha__                    
    int space;                        //占空间大小
#endif
    struct objc_ivar {                //每一个ivar的数据结构
        char *ivar_name;            //变量名
        char *ivar_type;            //变量类型
        int ivar_offset;            //偏移地址
#ifdef __alpha__
        int space;                    //占用空间
#endif
    } ivar_list[1];            /* variable length structure */
};
6 objc_method_list

objc_method_list 本质是一个有 objc_method 元素的可变长度的数组

struct objc_method_list {
#if defined(Release3CompatibilityBuild)
    struct objc_method_list *method_next;    //此变量用来连接另一个单独的方法列表
#else
    struct objc_method_list *obsolete;      //函数列表
#endif

    int method_count;                        //方法数量
#ifdef __alpha__
    int space;                                //空间大小
#endif
    struct objc_method {                    //method结构体
        SEL method_name;                    //方法名称
        char *method_types;                    //方法类型
        IMP method_imp;                        //方法实现
    } method_list[1];        /* variable length structure */
};
7 objc_cache

objc_class结构体中的cache字段用于缓存调用过的method。cache指针指向objc_cache结构体,objc_msgSend 每调用一次方法后,就会把该方法缓存到cache列表中,下次的时候,就直接优先从cache列表中寻找,如果cache没有,才从methodLists中查找方法。

typedef struct objc_method *Method;

struct objc_cache {
    //指定分配缓存bucket的总数。runtime使用这个字段确定线性查找数组的索引位置
    unsigned int mask;            /* total = mask + 1 */  
    //实际占用缓存bucket总数
    unsigned int occupied;    
    //指向Method数据结构指针的数组,这个数组的总数不能超过mask+1,但是指针是可能为空的,这就表示缓存bucket没有被占用,数组会随着时间增长
    Method buckets[1];
};

struct objc_method_description {     //方法的描述
    SEL name;                       //方法名称
    char *types;                    //方法类型
};

SEL ,俗称方法选择器,实质存储的是方法的名称

IMP ,俗称方法实现,看源码得知它就是一个函数指针

Method 对上述两者的一个包装结构.

8 objc_protocol_list

存储协议的链表,其实 可以看到Protocol是一个objc_object类型

typedef struct objc_object Protocol;

struct objc_protocol_list {
    struct objc_protocol_list *next;        //方法列表
    int count;                              //protocol总数
    Protocol *list[1];                        //Protocol的
};
9 struct objc_category

Category是表示一个指向分类的结构体的指针,这个结构体主要包含了分类定义的实例方法与类方法,其中instance_methods列表是objc_class中方法列表的一个子集,而class_methods列表是元类方法列表的一个子集。

typedef struct objc_category *Category;

objc_category {
    //类别名称
    char *category_name                                      OBJC2_UNAVAILABLE;
    //类名
    char *class_name                                         OBJC2_UNAVAILABLE;
    //实例方法列表
    struct objc_method_list *instance_methods                OBJC2_UNAVAILABLE;
    //类方法列表
    struct objc_method_list *class_methods                   OBJC2_UNAVAILABLE;
    //协议列表
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
}                                                            
10 objc_property

objc_property_t是表示objective-C 声明的属性的类型,是指向objc_property的结构图指针,与之关联的还有一个objc_property_attribute_t,它是属性的attribute,也就是其实是对属性的详细描述,包括属性名称、属性编码类型、原子类型/非原子类型等

typedef struct objc_property *objc_property_t;

typedef struct {
const char *name;           //关联的名称
const char *value;          //关联的值(通常为空)
} objc_property_attribute_t;

后面的篇章我会带来关于 objc_msgsend 的知识点