美文网首页OC底层相关
《OC理论底层详解篇之runtime运行时 》

《OC理论底层详解篇之runtime运行时 》

作者: 不够果断是种癌 | 来源:发表于2019-03-04 16:54 被阅读14次

    谈到runtime我们不免想到isa指针。那什么是isa指针呢?OC语言里面每个对象都会有一个隐藏的数据结构,这个数据结构同时是OC对象的第一个成员变量。其实这个数据结构就是我们的isa指针。

    那么isa指针有什么作用呢?那我们接下来看一下isa的指针的作用。

     isa指针通常用来指向对象所属的类,从而通过调用方法时通过isa指针找到相应的方法和属性。(元类的isa指针指向的就是元类)。

    接下来我们看一下isa历史优化。

    在arm64架构之前,isa就是一个普通的指针,储存着Class,Meta-Class对象的内存地址。

    从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来储存更多的信息。

    接下来我们谈谈位域位域储存了哪些信息?

    首先我们看一下 nonpointer 这个里面存在0种情况 如果是0,就是说明是arm64架构之前。1代表已经优化过了,也就是arm64架构之后的。

    接下来我们看一下has_assoc  这个作用是否有设置过关联对象,如果没有释放时会更快。

    接下来我们看一下has_cxx_dtor  这个作用是否有C++析构函数,如果没有释放会更快。

    接下来我们看一下shiftcls   这个里面储存着Class,Meta-Class对象的内存地址。

    接下来我们看一下  magic  这个用于在调试时分辨对象是否完成初始化。

    接下来再看一下 weakly_referenced  这个是判断是否被弱引用指向过,如果没有,释放时会更快。

    接下来再看一下   deallocating  这个主要判断对象是否正在释放。

    接下来再看一下   extra_rc 里面储存的值是引用计数减1

    接下来再看一       has_sidetable_rc  判断引用计数器是否过大无法储存在isa中,如果为1那么引用计数会储存在一个叫SideTable的类的属性中。

    然后我们看一下Class结构

    接下来我们分析一下  class_rw_t。这个里面的方法,属性,协议是二维数组,可读可写,包含了类的初始化内容,分类的内容。

    接下来我们分析一下  class_ro_t。 这个里面基类方法,基类协议,基类属性,成员变量,是只读的,包含了类的初始化内容。

    接下来我们分析一下method_t。 这个是对方发/函数的封装。接下来我们看一下method_t的样子

    SEL代表方法/函数名,一般叫做选择器。

    可以通过@selector()和sel_registerName()获得。

    可以通过sel_getName()和NSStringFromSelector()转成字符串。

    不同类中的相同名字的方法,所对应的方法选择器是一样的。 我们看一下 typedef struct objc_selector * SEL 。

    types包含了函数的返回值,参数编码的字符串。

    接下来我们谈一下方法缓存  也就是cache_t  接下来我们看一下它的样子

    其内部是用散列表(哈希表)  来缓存曾经调用的方法,从而提高方法的速度。

    isa我们暂时介绍这么多。接下来重头戏来了,也就是我们的消息转发机制。哦,不对还没有谈runtime? 什么是runtime?

    OC是一门动态性比较轻的编程语言,允许很多操作推迟到程序运行时再近些,OC的动态性就是由runtime来支撑和实现的,runTime是一套C语言的API,封装了很多动态性相关的函数,平时编写的OC代码,底层都是转换成了Runtime API进行调用。

    runtime的具体应用

    利用关联对象(AssociatedObject) 给分类添加属性

    遍历类的所有成员变量 (修改textfield的占位文字的颜色,字典转模型,自动归档解档)

    交换方法实现(交换系统的方法)

    利用消息转发机制解决方法找不到的异常问题

    OC中的方法调用,其实都是转换为objc_msgSend函数的调用。

    其流程大致分为,消息发送(当前类,父类中查找),动态方法解析,消息转发三大阶段。

    OC中的方法调用其实都是转成了objc_msgSend函数的调用,receiver(方法调用者)发送了一条消息(selector方法名)

    我们先看第二个流程---------消息发送(当前类,父类中查找)

    我们首先判断当前方法的调用者是否为存在,如果不存在我们则去看方法调用者的父类是否存在。

    如果当前类存在-》我们就去方法的缓存中找-》方法里面去找

    // 下面这条链找到了方法就会缓存到缓存方法里面

    -》父类缓存方法中找  -》父类方法中找

    //  已经排序的,二分查找法    没有排序的,遍历查找  

    //  调用者通过isa指针找到调用者的类

    //  调用者通过父类指针找到父类

    我们接下来看第一个流程    动态方法解析 

    如果调用了对象方法首先会进行+(BOOL)resolveInstanceMethod:(SEL)sel判断

    如果调用了类方法 首先会进行 +(BOOL)resolveClassMethod:(SEL)sel判断

    同时上面2个方法来动态添加方法实现

    动态解析后,会重新走"消息发送"的流程

    从当前类的缓存方法中查找方法开始

    最后我们来看消息转发的第三个流程  消息转发

    //  快速转发 返回值===nil 则往下走

    -(id)forwardingTargetForSelector:(SEL)aSelector{}

    //  常规转发

    -(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector

    相关文章

      网友评论

        本文标题:《OC理论底层详解篇之runtime运行时 》

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