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

2021-09-02  本文已影响0人  WallisW

前世缘

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

我们在看一下objc_msgSend的结构:


// 调用run方法,传入100参数
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)person, sel_registerName("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是前段部分是用汇编实现的,那么为什么要用汇编?

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

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

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

--20210901子时



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

上一篇下一篇

猜你喜欢

热点阅读