携手创作,一起成长!这是我参与「日新计划 8 月更文应战」的第4天, 点击检查活动概况

导言

上一篇中说到实例办法缓存在类的cache_t中,是通过供给的inset(sel, imp, cls)讲办法缓存起来,咱们直接拦腰截断从插入办法开起来的,但是咱们并不知道是谁引起的insert()也就没有办法形成闭环,下面要探究的便是何时动身的insert()。 首要在源码中insert打下断点,检查堆栈信息

iOS 底层原理之objc_msgSend(上)
_objc_msgSend_uncached -> lookUpImpOrForward -> log_and_fill_cache -> insert,依据这个流程,能够这样了解么?”首次调用时办法没有找到,开端寻找办法,终究把办法插入到缓存中”,这儿是个问句,需求探究

调用办法底层完成

main.m文件的完成如下:

iOS 底层原理之objc_msgSend(上)
这儿想知道[person saySomething]音讯发送在底层做了什么,运用clang指令转换成c++,在main.m地点文件夹履行以下指令:

clang -rewrite-objc main.m -o main.cpp

履行完成后,会看到一个和main.m同级的main.cpp文件,翻开并找到DXJPerson的完成

iOS 底层原理之objc_msgSend(上)
上图中能够发现:

  1. [person saySomething]在OC层面办法完成是没有带参数的,其实在底层是现实过程中带了两个躲藏参数DXJPerson * self, SEL _cmd
  2. [person saySomething]在底层是这样发送音讯的
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("saySomething"));
简化后:
objc_msgSend(person, sel_registerName("saySomething"));

objc_msgSend()是音讯发送的底层完成,那是不是咱们也能够运用该api发送音讯呢?答案是能够的,看下图

iOS 底层原理之objc_msgSend(上)

objc_msgSend()过程中,发现了另一个有意思的函数objc_msgSendSuper(),他是这样界说的,内部也有两个躲藏参数,一个是常见的sel,另一个是objc_super *类型的结构体指针

objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )

翻开objclib.A.dylib源码库,看下objc_super结构体内部完成有两个特点:

  • receiver : 音讯的接受者
  • super_class : the first class to search(首要从哪个类开端被检索办法)

如下图:

iOS 底层原理之objc_msgSend(上)

依据objc_msgSendSuper()的界说,运用该函数去发送一个音讯:

iOS 底层原理之objc_msgSend(上)

objc_msgSend之汇编流程

到这儿已经知道了音讯的发送是通过objc_msgSend()完成,要想知道底层是完成,首要通过汇编跟流程,在[person saySomething]打下断点,然后挑选Debug->Debug workflow ->Always show Disassembly(这儿有打断点的几种方式)

iOS 底层原理之objc_msgSend(上)

objc_msgSend是归于libobjc.A.dylib库,翻开该源码库,全局查找objc_msgSend办法,因为该函数的底层是用汇编完成的所以只用找.s文件即可

iOS 底层原理之objc_msgSend(上)
下面剖析一下汇编中objc_msgSend的完成

iOS 底层原理之objc_msgSend(上)

  1. cmp p0, #0, 依据源码供给的解释,p0是接受者的isa,拿 p0#0比较,是检测接受者是否是nil或者 tagged pointer类型
  2. ldr p13, [x0]x0 receive的isa地址,将[x0] 地址移入到p13
  3. GetClassFromIsa_p16 p13, 1, x0, 下图是GetClassFromIsa_p16的完成,红框中表示的是不同架构类型下的操作,想要做的事情是一样的,所以这儿只拿一种做剖析else流程
    iOS 底层原理之objc_msgSend(上)
    ExtractISA p16, \src, \auth_address,src便是传递过来isa,依据下方源码的完成,将isa & ISA_MASK 赋值给p16,至此类的地址获取到了,放在p16
    .macro ExtractISA
    and $0, $1, #ISA_MASK // 将($1 & ISA_MASK)然后赋值给$0,p16 = $0 , \src = $1, \auth_address = $2
    
  4. CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached至此开端CacheLookup流程