本文由快学吧个人写作,以任何形式转载请表明原文出处

一、材料预备

objc4-818.2

对应mac的版本是11.1。可根据自己的系统版本挑选可以进行调试的源码

二、思路

  1. 为什么要看objc_msgSend?

调用办法的实质便是objc_msgSend

  1. objc_msgSend在哪里?

在runtime中

  1. objc_msgSend是什么写的?

汇编

  1. objc_msgSend为什么用汇编?
  1. C言语是静态言语。不行能写一个函数用来保留不知道的参数,还要做到跳转任一函数的指针。

  2. 汇编是更挨近机器指令的言语,objc_msgSend的功用就决定了它需要更快。

  1. 为什么要用objc4的源码?

由于objc_msgSend的源码是在libobjc.A.dyld库中,objc4里有这个库。

三、创立代码

  1. 在818.2中创立一个类(我创立的是JDPerson),继承于NSObject。而且界说一个实例办法。然后在main.m中创立实例,并调用这个实例办法。并在调用实例办法这一行打断点。

十二、objc_msgSend(方法在缓存中的查找流程)

十二、objc_msgSend(方法在缓存中的查找流程)

  1. 翻开汇编

十二、objc_msgSend(方法在缓存中的查找流程)

  1. 运行项目

四、有关汇编的简单根底

  1. 汇编中有个东西叫寄存器,arm64架构下有31个通用寄存器,每个寄存器都是64位。访问它们用:x0x30

  2. 访问寄存器的低32位用 :w0w30

  3. 寄存器的x0到x7存储的是函数的前8个参数。例如:objc_msgSend的参数是id selfSEL sel,分别对应着x0和x1。别的x0还是返回值的存储方位。

五、objc_msgSend的源码

1. 如何找到objc_msgSend的源码

在818.2的源码中查找objc_msgSend,找到arm64环境下的objc_msgSend,并在其间找到带ENTRYobjc_msgSend,由于ENTRY是进入的意思。

十二、objc_msgSend(方法在缓存中的查找流程)

2. objc_msgSend汇编解析

1. ENTRY _objc_msgSend,对消息接收者(id self,sel _cmd)判别处理,tagged pointer判别处理。

十二、objc_msgSend(方法在缓存中的查找流程)

2. GetClassFromIsa_p16关于isa指针的处理,获得class(类)。

文件内查找GetClassFromIsa_p16。找到源码后,对其间的#if SUPPORT_INDEXED_ISA不需要看它里面的内容,找__LP64__

十二、objc_msgSend(方法在缓存中的查找流程)

这里的ExtractISA还不知道是什么,大局查找ExtractISA :

十二、objc_msgSend(方法在缓存中的查找流程)

这里的$1便是isa$0= $1 & #ISA_MASK。也便是类信息。

3. 回到主线,CacheLookup查找缓存

十二、objc_msgSend(方法在缓存中的查找流程)

(1). CacheLookup的注释 :

十二、objc_msgSend(方法在缓存中的查找流程)

(2). 一些宏界说,CacheLookup源码中会用到的 :

十二、objc_msgSend(方法在缓存中的查找流程)

十二、objc_msgSend(方法在缓存中的查找流程)

十二、objc_msgSend(方法在缓存中的查找流程)

十二、objc_msgSend(方法在缓存中的查找流程)

(3). CacheLookup源码解析 :

十二、objc_msgSend(方法在缓存中的查找流程)

十二、objc_msgSend(方法在缓存中的查找流程)

十二、objc_msgSend(方法在缓存中的查找流程)

十二、objc_msgSend(方法在缓存中的查找流程)

十二、objc_msgSend(方法在缓存中的查找流程)

(4). CacheHit射中缓存的源码 :

首要清晰一点,CacheLookupNORMAL

十二、objc_msgSend(方法在缓存中的查找流程)

直接调用了一个TailCallCachedImp

(5).

十二、objc_msgSend(方法在缓存中的查找流程)

六、总结

  1. objc_msgSend会对消息的接收者进行判别处理。
  2. objc_msgSend中的GetClassFromIsa_p16会对isa进行处理,获取其间的类信息。
  3. objc_msgSend会在CacheLookup中经过类的内存平移获取到缓存,在缓存中拿到mask和buckets,经过掩码获取到单独的buckets。经过cache_hash获取sel的索引,经过遍历buckets中的bucket_t,查找缓存中是否有要调用的办法的完成。