美文网首页
iOS开发Runtime的理解与应用

iOS开发Runtime的理解与应用

作者: 小呀小苹果呀 | 来源:发表于2018-04-09 17:33 被阅读0次

    一、Runtime概念

    • RunTime简称运行时,其中最主要的是消息机制。

    • 对于C语言,函数的调用在编译的时候会决定调用哪个函数,编译完成之后直接顺序执行,无任何二义性。

    • OC的函数调用成为消息发送,属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。而C语言在编译阶段就会报错)。

    • 只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。

      其动态性体现在三个方面:

    1.动态类型:
    即运行时再决定对象的类型。简单说就是id类型,任何对象都可以被id指针所指,只有在运行时才能决定是什么类型。像内置的明确的基本类型都属于静态类型(int、NSString等)。静态类型在编译的时候就能被识别出来。所以,若程序发生了类型不对应,编译器就会发出警告。而动态类型就编译器编译的时候是不能被识别的,要等到运行时(run time),即程序运行的时候才会根据语境来识别。所以这里面就有两个概念要分清:编译时跟运行时。
    2.动态绑定:
    基于动态类型,在某个实例对象被确定后,其类型便被确定了。该对象对应的属性和响应的消息也被完全确定,这就是动态绑定。比如我们一般向一个NSObject对象发送-respondsToSelector:或者 -instancesRespondToSelector:等来确定对象是否可以对某个SEL做出响应,而在OC消息转发机制被触发之前,对应的类 的+resolveClassMethod:和+resolveInstanceMethod:将会被调用,在此时有机会动态地向类或者实例添加新的方 法,也即类的实现是可以动态绑定的;isKindOfClass也是一样的道理。
    3.动态加载:
    所谓动态加载就是我们做开发的时候icon图片的时候在Retina设备上要多添加一个张@2x的图片,当设备更换的时候,图片也会自动的替换。

    二、Runtime数据结构

    1.Class

    Objective-C类是由Class类型来表示,Class 其实是指向 objc_class 结构体的指针

    typedef struct objc_class *Class;
    

    我们可以从<objc/runtime.h>里面看到类的定义

    struct object_class{
        Class isa OBJC_ISA_AVAILABILITY;
    #if !__OBJC2__
         Class super_class                        OBJC2_UNAVAILABLE;  // 父类
         const char *name                         OBJC2_UNAVAILABLE;  // 类名
         long version                             OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0
         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;
    
    2.id

    id 是一个结构体指针类型,它可以指向 Objective-C 中的任何对象。从<objc/objc.h>中可以看出id其实是指向objc_object结构体的指针。objc_object只有一个成员变量 isa,对象可以通过 isa 指针找到其所属的类,这也是为什么id可以表示任意对象的原因。isa 是一个 Class 类型的成员变量。
    注意:在KVO中,isa在运行时会被修改,指向一个中间类,对于编译器而言,isa的指向才是最真实的类型。

    /// Represents an instance of a class.
    struct objc_object {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    
    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
    

    当我们向一个Objective-C对象发送消息时,运行时库会根据
    实例对象的isa指针找到这个实例对象所属的类。Runtime库会在类的方法列表由super_class指针找到父类的方法列表直至根类NSObject中去寻找与消息对应的selector指向的方法,找到后即运行这个方法。

    3.元类(meta-Class)

    类自身也是一种对象,可以叫做“类对象”。类对象包含一个指向其类的一个isa指针( Class _Nonnull isa )

    为了调用类方法,这个类的isa指针必须指向一个包含这些类方法的一个objc_class结构体。这就引出了meta-class的概念,meta-class中存储着一个类的所有类方法。
    所以,调用类方法的这个类对象的isa指针指向的就是meta-class。当我们向一个对象发送消息时,runtime会在这个对象所属的这个类的方法列表中查找方法;而向一个类发送消息时,会在这个类的meta-class的方法列表中查找。

    下图是一个经典的类及相应meta-class类的一个继承体系图解:

    image.png

    从图中可以看出:
    1.每个实例对象的类都是类对象,每个类对象的类都是元类对象,每个元类对象的类都是根元类(root meta class的isa指向自身)。即任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。
    2.类对象的父类最终继承自根类对象NSObject,NSObject的父类为nil
    3.元类对象(包括根元类)的父类最终继承自根类对象NSObject

    三、方法和消息

    1.SEL
    typedef struct objc_selector *SEL;
    
    SEL sel1 = @selector(func1);
    SEL sel2 = NSSelectorFromString(func2);
    

    SEL本质上是一个指向方法的指针。Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识即SEL,每一个方法都对应着一个SEL。所以即使返回值类型或参数类型不同,方法名相同也会报错。

    2.IMP
    id (*IMP)(id, SEL,...)
    

    IMP的本质是函数指针,指向方法实现的地址,直接通过IMP就可以找到各个方法。这样效率更高,因为绕过了消息传递阶段,直接定位。
    SEL就是为了查找方法的最终实现IMP的。由于每个方法对应唯一的SEL,因此我们可以通过SEL方便快速准确地获得它所对应的IMP。

    消息发送和转发流程可以概括为:消息发送(Messaging)是 Runtime 通过 selector 快速查找 IMP 的过程,有了函数指针就可以执行对应的方法实现;消息转发(Message Forwarding)是在查找 IMP 失败后执行一系列转发流程的慢速通道,如果不作转发处理,则会打日志和抛出异常。

    3.Method
    typedef struct objc_method *Method
    struct objc_method{
        SEL method_name      OBJC2_UNAVAILABLE; // 方法名
        char *method_types   OBJC2_UNAVAILABLE;
        IMP method_imp       OBJC2_UNAVAILABLE; // 方法实现
    }
    

    可以看出method包含SEL和IMP,在实现方法交换时,主要原理就是交换SEL和IMP的映射关系。

    参考:https://www.jianshu.com/p/46dd81402f63
    https://www.jianshu.com/p/adf0d566c887
    https://www.jianshu.com/p/13457a27624c

    相关文章

      网友评论

          本文标题:iOS开发Runtime的理解与应用

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