As we know, OC上层的方法调用的底层逻辑都是通过objc_msgSend来实现的,那么如何验证呢?我们通过clang反编译看看就知道了:
上层的OC调用:
|
|
使用clang命令:
|
|
反编译之后的底层实现:
|
|
可以看到,所有的方法调用全都被编译器转换为了objc_msgSend方法的调用,我们很自然的去runtime源码中找该函数的实现逻辑,结果在.mm文件中没有发现,以为线索就要断了的时候,在objc-msg-arm64.s文件内看到了下面几行代码:
|
|
这里应该就是objc_msgSend的入口了,原来是使用汇编代码来实现的,只能硬着头皮看下去了。
上面的逻辑最终调用了CacheLookup这个函数,我们全局搜一下,找到了该函数的实现部分:
|
|
.macro CacheHit
.if $0 == NORMAL
TailCallCachedImp x17, x12, x1, x16 // authenticate and call imp
.elseif $0 == GETIMP
mov p0, p17
cbz p0, 9f // don’t ptrauth a nil imp
AuthAndResignAsIMP x0, x12, x1, x16 // authenticate imp and re-sign as IMP
9: ret // return IMP
.elseif $0 == LOOKUP
// No nil check for ptrauth: the caller would crash anyway when they
// jump to a nil IMP. We don’t care if that jump also fails ptrauth.
AuthAndResignAsIMP x17, x12, x1, x16 // authenticate imp and re-sign as IMP
ret // return imp via x17
.else
.abort oops
.endif
.endmacro
|
|
.macro JumpMiss
.if $0 == GETIMP
b LGetImpMiss
.elseif $0 == NORMAL
b objc_msgSend_uncached
.elseif $0 == LOOKUP
b objc_msgLookup_uncached
.else
.abort oops
.endif
.endmacro
```
都只是根据类型不同调用了对应的方法,这里已经不再是快查询的逻辑,我们暂时不看。
小结
这里先点个题,所谓的快查找就是对cache的查找,与之对应的慢查找就是对method_list的查找了,method_list的查找可能涉及到不同文件的遍历过程,所以会比cache慢很多。通过查看汇编实现,我的主要感受就是对类结构的熟悉是非常重要的,因为所有的查找逻辑都是基于类的结构来的。我们在实际编码过程中,也应该做到庖丁解牛,而不是盲人摸象。