Runtime小笔记(一)

作者: 欣东 | 来源:发表于2016-05-13 11:42 被阅读374次

    前言

    之前看过关于runtime的几篇文章,水平各有千秋,现在先汇集各个大牛的总结,以后找个时间将Runtime的源码啃一遍,再完善自己的笔记

    什么是Runtime

    Objective-C Runtime是一个将C语言转化为面向对象语言的扩展。
    C++和Objective进行对比:
    同 : C++和Objective-C都是在C的基础上加入面向对象的特性扩充而成的程序设计语言;
    异 : 实现的机制差异不同。C++是基于静态类型,而Objective-C是基于动态运行时类型。也就是说用C++编写的程序编译时就直接编译成了可令机器读懂的机器语言;用Objective-C编写的程序不能直接编译成可令机器读懂的机器语言,而是在程序运行的时候,通过Runtime把程序转为可令机器读懂的机器语言。Runtime是Objective不可缺少的重要一部分;

    一些 Runtime 的术语

    id 和 Class

    #if !OBJC_TYPES_DEFINED
    /// An opaque type that represents an Objective-C class.
    typedef struct objc_class *Class;
    
    /// Represents an instance of a class.
    struct objc_object {
        Class isa  OBJC_ISA_AVAILABILITY;
    };
    
    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
    #endif
    

    Class是一个指向==objc_class==结构体的指针;
    id是一个指向==objc_object==结构体的指针,其中的==isa==是一个指==objc_class==结构体的指针。
    换句话说,id就是我们所说的对象,Class就是我们所说的类。

    objc_class的定义

    typedef struct objc_class *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; 
     struct objc_cache *cache                  OBJC2_UNAVAILABLE; 
     struct objc_protocol_list *protocols      OBJC2_UNAVAILABLE; 
    #endif
    }
    /* Use `Class` instead of `struct objc_class *` */
    

    isa指针:指向的类结构称为metaclass(元类),其中存放着static类型的成员变量与类方法(“+”开头的方法);
    super_class:指向该类的父类的指针,如果该类是根类(如NSObject或NSProxy),那么super_class就为nil;
    name:类名;
    version:类的版本信息,默认为0,可以通过runtime函数class_setVersion或者class_getVersion进行修改、读取;
    info:类信息,供运行时期使用的一些位标识,如CLS_CLASS (0x1L) 表示该类为普通 class,其中包含实例方法和变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
    instance_size:该类的实例变量大小(包括从父类继承下来的实例变量);
    ivars:该类的成员变量地址列表;
    methodLists:方法地址列表,与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储实例方法,如CLS_META (0x2L),则存储类方法;
    cache:缓存最近使用的方法地址,用于提升效率;
    protocols:存储该类声明遵守的协议的列表;

    类与对象的继承层次关系如图:

    objective-runtime.png

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

    SEL

    SEL是方法选择器,作用就和名字一样,日常生活中,我们通过人名辨别谁是谁,注意 Objc 在相同的类中不会有命名相同的两个方法。selector 对方法名进行包装,以便找到对应的方法实现

    typedef struct objc_selector *SEL;
    
    struct objc_selector {
        char *name;                       OBJC2_UNAVAILABLE;// 方法名
        char *types;                      OBJC2_UNAVAILABLE;// 方法类型
    };
    

    IMP

    IMP是由编译器生成的一个函数指针,指向方法的实现。当你发起一个消息后,这个函数指针决定了最终执行哪段代码。

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

    Method

    Method代表类中的某个方法的类型。

    typedef struct objc_method *Method;
    
    struct objc_method {
        SEL method_name                   OBJC2_UNAVAILABLE; // 方法名
        char *method_types                OBJC2_UNAVAILABLE; // 方法类型,存储着方法的参数类型和返回值类型
        IMP method_imp                    OBJC2_UNAVAILABLE; // 方法实现
    }
    

    Ivar

    Ivar代表类中实例变量的类型

    typedef struct objc_ivar *Ivar;
    
    struct objc_ivar {
        char *ivar_name                   OBJC2_UNAVAILABLE; // 变量名
        char *ivar_type                   OBJC2_UNAVAILABLE; // 变量类型
        int ivar_offset                   OBJC2_UNAVAILABLE; // �基地址偏移字节
    #ifdef __LP64__
        int space                         OBJC2_UNAVAILABLE; // 占用空间
    #endif
    }
    
    

    objc_property_t

    objc_property_t是属性,是内置的类型,与之关联的还有一个objc_property_attribute_t,它是属性的attribute,也就是其实是对属性的详细描述,包括属性名称、属性编码类型、原子类型/非原子类型等。它的定义如下

    typedef struct objc_property *objc_property_t;
    
    typedef struct {
        const char *name; // 名称
        const char *value;  // 值(通常是空的)
    } objc_property_attribute_t;
    

    Cache

    方法地址缓存

    typedef struct objc_cache *Cache
    
    struct objc_cache {
        unsigned int mask                   OBJC2_UNAVAILABLE;
        unsigned int occupied               OBJC2_UNAVAILABLE;
        Method buckets[1]                   OBJC2_UNAVAILABLE;
    };
    

    mask: 指定分配cache buckets的总数。在方法查找中,Runtime使用这个字段确定数组的索引位置。
    occupied: 实际占用cache buckets的总数。
    buckets: 指定Method数据结构指针的数组。这个数组可能包含不超过mask+1个元素。需要注意的是,指针可能是NULL,表示这个缓存bucket没有被占用,另外被占用的bucket可能是不连续的。这个数组可能会随着时间而增长。
    objc_msgSend每调用一次方法后,就会把该方法缓存到cache列表中,下次调用的时候,就直接优先从cache列表中寻找,如果cache没有,才从methodLists中查找方法。

    Catagory

    这个就是我们平时所说的类别了,它可以动态的为已存在的类添加新的方法。

    typedef struct objc_category *Category;
    
    struct 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; // 协议列表
    }
    

    参考文章:
    Objective-C Runtime 1小时入门教程
    详解Runtime运行时机制

    相关文章

      网友评论

      本文标题:Runtime小笔记(一)

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