通过clang将OC代码翻译成C++代码之后,我们发现,OC的方法调用的本质是消息发送的过程
clang -rewrite-objc main.m -o main.cpp
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
LGTeacher *teach = ((LGTeacher *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGTeacher"), sel_registerName("alloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayNB"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHello"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_hr_l_56yp8j4y11491njzqx6f880000gn_T_main_c501bc_mi_2);
}
return 0;
}
所有的秘密从
objc_msgSend
开始,下面从底层源码分析一下objc_msgSend
都做了什么。首先从上面的clang输出代码可以看出,objc_msgSend
调用的第一个参数是消息接收者
,第二个参数是SEL
。从objc-msg-arm64.s
文件中找到了objc_msgSend
的汇编源码,下面代码中的中文注释解释了每条重要指令的意义
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
// p0 = 消息接收者,这里消息接收者和0比较
cmp p0, #0 // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
// 如果小于等于0,跳到LNilOrTagged
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
// 如果等于0,跳到LReturnZero,其实就返回0
b.eq LReturnZero
#endif
// 类偏移0的位置存储的是isa,isa放到p13
ldr p13, [x0] // p13 = isa
GetClassFromIsa_p16 p13, 1, x0 // p16 = class
LGetIsaDone:
// calls imp or objc_msgSend_uncached
CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
// 如果等于0,返回0
b.eq LReturnZero // nil check
// 这个跳转先放一放,下回分解
GetTaggedClass
b LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
ret
END_ENTRY _objc_msgSend
下一步跳转到GetClassFromIsa_p16宏
GetClassFromIsa_p16 p13, 1, x0 // p16 = class
.macro GetClassFromIsa_p16 src, needs_auth, auth_address /* note: auth_address is not required if !needs_auth */
#if SUPPORT_INDEXED_ISA // 不走这里
// Indexed isa
mov p16, \src // optimistically set dst = src
tbz p16, #ISA_INDEX_IS_NPI_BIT, 1f // done if not non-pointer isa
// isa in p16 is indexed
adrp x10, _objc_indexed_classes@PAGE
add x10, x10, _objc_indexed_classes@PAGEOFF
ubfx p16, p16, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS // extract index
ldr p16, [x10, p16, UXTP #PTRSHIFT] // load class from array
1:
#elif __LP64__
// 传入的needs_auth = 1
.if \needs_auth == 0 // _cache_getImp takes an authed class already
mov p16, \src
.else
// 直接跳到这里
// 64-bit packed isa
ExtractISA p16, \src, \auth_address
.endif
#else
// 32-bit raw isa
mov p16, \src
#endif
.endmacro
SUPPORT_INDEXED_ISA = 0
,所有我们进入__LP64__
的代码段,我们传人的needs_auth = 1
,所以直接跳到ExtractISA
ExtractISA p16, \src, \auth_address
// p16 = isa & ISA_MASK
.macro ExtractISA
// $0 = p16, $1 = isa,isa & ISA_MASK = 类地址
// p16 = 类地址
and $0, $1, #ISA_MASK
.endmacro
GetClassFromIsa_p16
其实就是通过isa & ISA_MASK
取到类地址,类地址放到p16里
取到类地址之后,跳转到CacheLookup
,
CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached
,这个宏我们在下一编文章中继续分析
未完待续