美文网首页
【OC梳理】runtime

【OC梳理】runtime

作者: 忠橙_g | 来源:发表于2018-03-18 23:12 被阅读18次

    什么是runtime

    runtime是属于OC的底层,可以进行一些非常底层的操作(用OC是无法现实的, 不好实现)。 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者。
    例如:

    // 调用无参数的方法:
    [receiver selector];
    // 运行时会被编译器转化为:
    objc_msgSend(receiver, selector)
    
    // 调用有参数的方法:
    [receiver selector:(id)arg...];
    // 运行时会被编译器转化为:
    objc_msgSend(receiver, selector, arg, ...)
    

    runtime的作用

    • 动态创建一个类(比如KVO的底层实现)
    • 动态地为某个类添加属性/方法。可以用于封装框架(扩展、修改) 这就是我们runtime机制的主要运用方向
    • 遍历一个类中所有的成员变量(属性)/所有方法。(比如字典–>模型:利用runtime遍历模型对象的所有属性, 根据属性名从字典中取出对应的值, 设置到模型的属性上;还有归档和解档,利用runtime遍历模型对象的所有属性)
    • 利用runtime实现使用Block回调的KVO
    • 利用runtime实现多播委托

    runtime相关文件与函数

    #import <objc/runtime.h>
    #import <objc/message.h>
    

    官方文档翻译有些地方不是很通顺,可以直接查看官方文档
    Objective-C Runtime这里有runtime文件中方法的中文注释,不甚清晰,有空再自己整理一份。

    runtime的使用

    有三种方式可以使用Runtime:

    • Objective-C 源代码
    • NSObject 方法
    • Runtime 方法
    Objective-C 源代码

    通常情况下我们写的Objective-C代码,编译时会自动生成包含runtime特性的数据结构(包含了class、category、protocol中定义的信息)和方法。

    NSObject类中定义的方法

    Cocoa中大部分的类都是继承自NSObejct(NSProxy类例外,是一个抽象类)。
    一些情况下,NSObject 类仅仅定义了完成某件事情的模板,并没有提供所需要的代码。例如 -description 方法,该方法返回类内容的字符串,该方法主要用来调试程序。NSObject 类并不知道子类的内容,所以它只是返回类的名字和对象的地址,NSObject 的子类可以重新实现。

    还有一些 NSObject 的方法可以从 Runtime 系统中获取信息,允许对象进行自我检查。例如:

    • class方法返回对象的类;
    • isKindOfClass: 和 -isMemberOfClass: 方法检查对象是否存在于指定的类的继承体系中(是否是其子类或者父类或者当前类的成员变量);
    • respondsToSelector: 检查对象能否响应指定的消息;
    • conformsToProtocol:检查对象是否实现了指定协议类的方法;
    • methodForSelector: 返回指定方法实现的地址。
    使用runtime库函数

    常用的接口如下:

    /// 获取类
    Class PersonClass = object_getClass([Person class]);
    
    /// SEL是selector在 Objc 中的表示:
    SEL oriSEL = @selector(test1);
    
    /// 获取类方法
    Method oriMethod = Method class_getClassMethod(Class cls , SEL name);
    
    /// 获取实例方法
    Method class_getInstanceMethod(Class cls , SEL name)
    
    /// 添加方法
    BOOL addSucc = class_addMethod(xiaomingClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
    
    /// 替换原方法实现
    class_replaceMethod(toolClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
    
    /// 交换两个方法
    method_exchangeImplementations(oriMethod, cusMethod);
    
    /// 获取一个类的属性列表(返回值是一个数组)
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    
    /// 获取一个类的方法列表(返回值是一个数组)
    Method *methodList = class_copyMethodList([self class], &count);
    
    /// 获取一个类的成员变量列表(返回值是一个数组)
    Ivar *ivarList = class_copyIvarList([self class], &count);
    
    /// 获取成员变量的名字
    const char *ivar_getName(Ivar v);
    
    /// 获取成员变量的类型
    const char *ivar_getTypeEndcoding(Ivar v);
    
    /// 获取一个类的协议列表(返回值是一个数组)
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
    
    /** 
      * set方法
      * 将值value 跟对象object 关联起来(将值value 存储到对象object 中)
      * 参数 object:给哪个对象设置属性
      * 参数 key:一个属性对应一个Key,将来可以通过key取出这个存储的值,key 可以是任何类型:double、int 等,建议用char 可以节省字节
      * 参数 value:给属性设置的值
      * 参数policy:存储策略 (assign 、copy 、 retain就是strong)
    **/
    void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy);
    
    /// 利用参数key 将对象object中存储的对应值取出来
    id objc_getAssociatedObject(id object , const void *key);
    

    runtime相关术语

    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;
    }
    
    SEL

    SEL是方法选择器(Swift中是Selector类),其实就是runtime中发送message时带的方法名(怀疑是一个string,类似className + method的组合,这也是OC为什么不能进行函数重载的原因)。
    OC中,performSelector系列方法所传的selector都是SEL的一个实例。

    IMP

    IMP是指向一个方法实现的函数指针,定义如下:

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

    id 是一个参数类型,它是指向某个类的实例的指针。

    Class

    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
    } OBJC2_UNAVAILABLE;
    

    从 objc_class 可以看到,一个运行时类中关联了它的父类指针、类名、成员变量、方法、缓存以及附属的协议。

    其中 objc_ivar_list 和 objc_method_list分别是成员变量列表和方法列表。

    Ivar

    Ivar 是表示成员变量的类型。包含了对象名称、对象类型,以及对象地址(基地址偏移字节)。

    Cache

    Cache 为方法调用的性能进行优化,每当实例对象接收到一个消息时,它不会直接在 isa 指针指向的类的方法列表中遍历查找能够响应的方法,因为每次都要查找效率太低了,而是优先在 Cache 中查找。

    Runtime 系统会把被调用的方法存到 Cache 中,如果一个方法被调用,那么它有可能今后还会被调用,下次查找的时候就会效率更高。就像计算机组成原理中 CPU 绕过主存先访问 Cache 一样。


    参考文章:
    runtime从入门到精通(一)—— 初识runtime
    runtime从入门到精通(二)—— 官方文档翻译
    runtime从入门到精通(三)—— runtime常用接口方法
    runtime从入门到精通(四)—— 一些runtime相关术语的数据结构

    iOS Runtime 详解
    iOS开发-Runtime详解
    OC运行时Runtime探究

    相关文章

      网友评论

          本文标题:【OC梳理】runtime

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