携手创作,一起成长!这是我参与「日新计划 8 月更文应战」的第4天, 点击检查活动概况
导言
上一篇中说到实例办法
缓存在类的cache_t
中,是通过供给的inset(sel, imp, cls)
讲办法缓存起来,咱们直接拦腰截断从插入办法开起来的,但是咱们并不知道是谁引起的insert()
也就没有办法形成闭环,下面要探究的便是何时动身的insert()
。
首要在源码中insert
打下断点,检查堆栈信息
_objc_msgSend_uncached -> lookUpImpOrForward -> log_and_fill_cache -> insert
,依据这个流程,能够这样了解么?”首次调用时办法没有找到,开端寻找办法,终究把办法插入到缓存中”,这儿是个问句,需求探究
调用办法底层完成
main.m
文件的完成如下:
这儿想知道[person saySomething]
音讯发送在底层做了什么,运用clang
指令转换成c++
,在main.m
地点文件夹履行以下指令:
clang -rewrite-objc main.m -o main.cpp
履行完成后,会看到一个和main.m
同级的main.cpp
文件,翻开并找到DXJPerson
的完成
上图中能够发现:
-
[person saySomething]
在OC层面办法完成是没有带参数的,其实在底层是现实过程中带了两个躲藏参数DXJPerson * self, SEL _cmd
-
[person saySomething]
在底层是这样发送音讯的
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("saySomething"));
简化后:
objc_msgSend(person, sel_registerName("saySomething"));
objc_msgSend()
是音讯发送的底层完成,那是不是咱们也能够运用该api发送音讯呢?答案是能够的,看下图
在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(首要从哪个类开端被检索办法)
如下图:
依据objc_msgSendSuper()
的界说,运用该函数去发送一个音讯:
objc_msgSend之汇编流程
到这儿已经知道了音讯的发送是通过objc_msgSend()
完成,要想知道底层是完成,首要通过汇编跟流程,在[person saySomething]
打下断点,然后挑选Debug->Debug workflow ->Always show Disassembly
(这儿有打断点的几种方式)
objc_msgSend
是归于libobjc.A.dylib
库,翻开该源码库,全局查找objc_msgSend
办法,因为该函数的底层是用汇编完成的所以只用找.s
文件即可
下面剖析一下汇编中objc_msgSend
的完成
-
cmp p0, #0
, 依据源码供给的解释,p0
是接受者的isa,拿p0
和#0
比较,是检测接受者是否是nil或者tagged pointer
类型 -
ldr p13, [x0]
,x0
receive的isa地址,将[x0]
地址移入到p13
中 -
GetClassFromIsa_p16 p13, 1, x0
, 下图是GetClassFromIsa_p16
的完成,红框中表示的是不同架构类型下的操作,想要做的事情是一样的,所以这儿只拿一种做剖析else
流程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
-
CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached
至此开端CacheLookup
流程