美文网首页底层知识iOS开发iOS 开发
runtime运行时机制初探秘(一)

runtime运行时机制初探秘(一)

作者: 呜啦啦啦拉拉 | 来源:发表于2016-08-28 10:36 被阅读52次

    Objective-C语言是一种面向对象的语言。是由底层的C语言以及汇编语言编写起来的一份框架。虽然C++与Objective-c语言一样都是由C语言扩展而来的,但是他们的实现还是有许多的不同。最大的不同是C++是静态的语言。而Objective-C语言是动态的。

    • 静态语言是指所有的方法调用逻辑实现都是在编译时就已经确定了。
    • 动态语言是指它的方法调用以及变量类型都是在运行的时候才开始确定。

    而实现该特性是一套机制,runtime机制。Objective-C语言的面向对象特性也是由runtime实现的。
    在objc/runtime.h文件中你可以看到他是这样定义Class的

    /// An opaque type that represents an Objective-C class.
    typedef struct objc_class *Class;
    

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

    关于Isa指针。苹果官方有一张图描述。

    449095-3e972ec16703c54d.png

    在实例变量中,isa指针指向该变量的类。在类中,isa指针指向该类的静态类。
    在静态类中,isa指针指向根类的静态类。

    • super_class 指向该类的父类
    • name 是一个字符串,里面存储的是类名
    • version 类的版本信息,默认为0。(具体干什么用的我也不知道)
    • info 类的信息,提供一些标识。比如CLS_CLASS (0x1L) 表示该类为普通 class,包含实例方法和变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法。
    • instance_size 表示该类的实例变量的长度(包括继承下来的)
    • ivars 表示该类的属性也就是成员变量的地址。
    • methodLists 表示该类的方法列表。
    • cache 运行过一次的方法会存在cache里面。再次运行时会先查找cache中的方法。提高效率。
    • protocols 储存该类声明的协议。

    在runtime中是这样定义ID的

    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
    

    objc_object 是一个结构体,里面只有一个isa指针

    /// Represents an instance of a class.
    struct objc_object {
        Class isa  OBJC_ISA_AVAILABILITY;
    };
    

    前面说了,该指针指向该实例对象的类。
    也就是当我们声明一个类时,其实是创建了一个 objc_class 结构体。而当我们创建一个实例对象时。其实是创建了一个结构体 objc_object 。里面有一个指针指向该对象的类 objc_class
    而方法在runtime中是怎样定义的呢。我在头文件中找到了这个

    /// An opaque type that represents a method in a class definition.
    typedef struct objc_method *Method;
    
    struct objc_method {
        SEL method_name                                          OBJC2_UNAVAILABLE;
        char *method_types                                       OBJC2_UNAVAILABLE;
        IMP method_imp                                           OBJC2_UNAVAILABLE;
    }
    

    里面有三个参数。第一个参数是 SEL 类型的。我在 objc.h 找到了它的定义

    /// An opaque type that represents a method selector.
    typedef struct objc_selector *SEL;
    

    很遗憾。我并没有在文件中以及官方文档中找到这个结构体的定义。(谁知道在哪有的可以跟我说一下0.0)这里应用一些别人找到的:

    struct objc_selector{
        char *name;                      OBJC2_UNAVAILABLE;
        char *types;                      OBJC2_UNAVAILABLE;
    }
    
    • name 我猜想应该是函数名称。
    • types 应该是该函数对应的C语言的类型。

    IMP 我找到了一段这样的定义:

    /// A pointer to the function of a method implementation. 
    #if !OBJC_OLD_DISPATCH_PROTOTYPES
    typedef void (*IMP)(void /* id, SEL, ... */ ); 
    #else
    typedef id (*IMP)(id, SEL, ...); 
    #endif
    

    IMP 是你所需要调用的函数的指针。
    这样,我们就能理解oc中基本的方法调用原理了。
    当我们调用一个实例对象 objmethod 方法

    [obj method];
    

    实际上就是对obj对象发送method消息。
    它会在编译时被编译成一段这样的代码:

    objc_msgSend(obj,@selector(method));
    

    另外还有几个函数与它对应

    • objc_msgSend_stret: 消息中有数据结构作为返回值(不是简单值)时,通过此函数发送和接收返回值
    • objc_msgSendSuper: 和objc_msgSend类似,这里把消息发送给父类的实例
    • ** objc_msgSendSuper_stret:**和objc_msgSend_stret类似,这里把消息发送给父类的实例并接收返回值

    而他的执行流程是

    1. 检测 obj 是否为nil,如果为nil则该消息会被忽略。
    2. 检测这个 selector 是不是要忽略的。
    3. 拿到 objisa 指针,通过 isa 指针找到该对象对应的类 obj_class。然后找到类以后检索 cache 中是否有 @selector(method) 对应的方法。如果没有则找 methodLists 方法列表,如果找到了,就讲该 SEL 加入 cache 中。如果没找到,再通过 super_class 检索父类。依次类推。最后找到根类如果还没检索到则发送 ERROR

    这个就是方法调用的全过程0.0如果有什么错误的地方请指正。

    相关文章

      网友评论

        本文标题:runtime运行时机制初探秘(一)

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