美文网首页
问道OC方法0x01之objc_msgSend的本质

问道OC方法0x01之objc_msgSend的本质

作者: WallisW | 来源:发表于2021-09-02 23:32 被阅读0次

    前世缘

    上回说道OC方法的本质,其底层实现是一个objc_msgSend的东东。那么这个objc_msgSend本质又是个啥?它是一个C或C++函数吗??如果不是,它又是怎么实现的呢???

    我们在看一下objc_msgSend的结构:

    
    // 调用run方法,传入100参数
    ((void (*)(id, SEL, int))(void *)objc_msgSend)((id)person, sel_registerName("run"), 100);
    
    

    他主要做了两件事:

    • 向一个person类发送一个run消息
    • 并附带一个100的参数

    我们知道OC所有的方法都是调用objc_msgSend,那么C语言能实现吗?要实现objc_msgSend需要具备两个条件:

    1.保存任意类型的未知参数
    2.在任意位置可以跳到对应指针

    显然C语言是做不到,那么runtime是怎么实现的???

    今生孽

    不入虎穴焉得虎子,讲到这里就必须去看看runtime源码了。我们去Apple的opensource下载runtime源码到本地,现在已经到818版本了,网上普遍流行的是750版本。在github也有一个解读runtime源码的project,大家有空可以去探究探究。

    打开我们下载的runtime项目,全局搜索objc_msgSend:


    image.png

    发现木有,有个.s文件。这是一个什么文件呢?.s是汇编文件的后缀,那么我们继续往下查看,我们以arm64为例:


    image.png

    虽然我们并不太懂汇编,但是基本看命名和注释也能猜个大概。看这里有个ENTRY明显应该是入口的意思,它会先进行一个tagged pointer的判断,然后主要走入了CacheLookup NORMAL:


    image.png

    注意这里的注释,很明显是在找缓存。从buckets中里面取元素,如果找到的和_cmd匹配就命中缓存CacheHit;否则则未命中缓存CacheMiss。从CacheLookup入口的注释得知,他的另一条路径便是objc_msgSend_uncached:

    image.png

    MethodTableLookup:顾名思义,是方法列表查找的意思。


    image.png

    继续一步步深入,再查看下__class_lookupMethodAndLoadCache3,发现并未找到__class_lookupMethodAndLoadCache3,但是找到了_class_lookupMethodAndLoadCache:

    /***********************************************************************
    * _class_lookupMethodAndLoadCache.
    * Method lookup for dispatchers ONLY. OTHER CODE SHOULD USE lookUpImp().
    * This lookup avoids optimistic cache scan because the dispatcher 
    * already tried that.
    **********************************************************************/
    IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
    {
        return lookUpImpOrForward(cls, sel, obj, 
                                  YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
    }
    

    哎,神奇了。我们终于从晦涩难懂的汇编跳到了还算熟悉的C++,之后便是C++的方法寻找函数了。

    回首 & 思考

    我们通过源码证实了objc_msgSend是前段部分是用汇编实现的,那么为什么要用汇编?

    • 首先,是上面说道的C并不能满足条件。而汇编拥有寄存器,可以满足objc_msgSend实现的两个核心条件
    • OC中所有的方法都是靠objc_msgSend实现的,使用汇编效率会相对更高

    总结一下,这里objc_msgSend主要核心是两个步骤:

    1.尝试从缓存里找方法,此时是使用了汇编实现
    2.缓存未命中,继续使用C++方法寻找

    故而,objc_msgSend的本质便是CacheLookup+lookUpImpOrForward。今天就探究到此,欲知后事如何,且听下回分解。。。

    --20210901子时



    我们必须像一座山,
    既满生着芳草香花,
    又有极坚硬的石头。
    ----------------------------- 老舍

    相关文章

      网友评论

          本文标题:问道OC方法0x01之objc_msgSend的本质

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