在了解objc_msgSend
之前,需要先了解runtime
。
Runtime是一套底层纯C语言API
,Objective-C
代码最终都会被编译器转化为运行时代码,通过消息机制决定函数调用方式。那么什么是运行时呢?
运行时就是程序已经装载在内存中
,与编译时不同的是,运行时在内存中做操作以及判断。
Objective-C
是一门动态语言
,就是因为它总是把一些决定性的工作从编译阶段推迟到运行时阶段。OC代码的运行不仅需要编译器,还需要运行时系统(Runtime Sytem)来执行编译后的代码。
runtime简析
官方文档介绍# Objective-C Runtime Programming Guide。
调用方式
Objective-C Code
-
Framework & Service
,例isKindofClass
-
Runtime API
,例class_getInstanceSize
下层调用
Compiler
,llvm
Runtime System Library
,运行时系统
探索objc_msgSend
-
创建一个
Command line Tool
工程,在main.m文件实现以下内容@interface Person : NSObject - (void)say; @end @implementation Person - (void)say{ NSLog(@"say"); } @end int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... Person *person = [Person alloc]; [person say]; } return 0; }
-
通过
Clang
将main.m
文件编译成main.cpp
注意 ios以及sdk版本取决于本地环境
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-14.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.0.sdk main.m
-
搜索
int main
,找到main
函数int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")); ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("say")); } return 0; }
sel_registerName = @selector()= NSSelectorFromString
-
objc_msgSend()
,发送的消息(消息的接受者,消息的主题)
通过objc_msgSend实现方法调用
-
引入
#import <objc/message.h>
头文件 -
在
objc_msgSend.pngbuildSettings
搜索msg_send
,设置为false
-
调用
objc_msgSend
objc_msgSend(person,sel_registerName("say"));
-
结果调用成功
调用objc_msgSend.png
通过objc_msgSendSuper调用父类方法
-
新增一个Teacher类继承于Person类。
@interface Teacher : Person @end @implementation Teacher @end
-
Person
类新增一个sayHello
方法- (void)sayHello; //... -(void)sayHello{ NSLog(@"sayHello"); }
-
初始化结构体
objc_super
,调用objc_msgSendSuper
Teacher *teacher = [Teacher alloc]; struct objc_super send_super; send_super.receiver = teacher; send_super.super_class = [Person class]; objc_msgSendSuper(&send_super, sel_registerName("sayHello"));
-
最终输出结果
objc_msgSendSuper结果.png
探索objc_msgSend
(快速查找流程)
objc_msgSend
源码
在objc
源码中找到objc-msg-arm64.s
文件,搜索_objc_msgSend
,找到ENTRY _objc_msgSend
。
//---- 消息发送 -- 汇编入口--objc_msgSend主要是拿到接收者的isa信息
ENTRY _objc_msgSend
//---- 无窗口
UNWIND _objc_msgSend, NoFrame
//---- p0 和空对比,即判断接收者是否存在,其中p0是objc_msgSend的第一个参数-消息接收者receiver
cmp p0, #0 // nil check and tagged pointer check
//---- le小于 --支持taggedpointer(小对象类型)的流程
#if SUPPORT_TAGGED_POINTERS
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
//---- p0 等于 0 时,直接返回 空
b.eq LReturnZero
#endif
//---- p0即receiver 肯定存在的流程
//---- 根据对象拿出isa ,即从x0寄存器指向的地址 取出 isa,存入 p13寄存器
ldr p13, [x0] // p13 = isa
//---- 在64位架构下通过 p16 = isa(p13) & ISA_MASK,拿出shiftcls信息,得到class信息
GetClassFromIsa_p16 p13 // p16 = class
LGetIsaDone:
// calls imp or objc_msgSend_uncached
//---- 如果有isa,走到CacheLookup 即缓存查找流程,也就是所谓的sel-imp快速查找流程
CacheLookup NORMAL, _objc_msgSend
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
//---- 等于空,返回空
b.eq LReturnZero // nil check
// tagged
adrp x10, _objc_debug_taggedpointer_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
ubfx x11, x0, #60, #4
ldr x16, [x10, x11, LSL #3]
adrp x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
cmp x10, x16
b.ne LGetIsaDone
// ext tagged
adrp x10, _objc_debug_taggedpointer_ext_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
ubfx x11, x0, #52, #8
ldr x16, [x10, x11, LSL #3]
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
-
cmp p0
判断对象是否为空; -
如果不支持
TAGGED_POINTERS
,则返回空; -
根据对象获取
isa
,最终获取class
为p16
; -
如果有isa,走到CacheLookup 即缓存查找流程,也就是所谓的
sel-imp
快速查找流程CacheLookup NORMAL, _objc_msgSend
CacheLookup
在文件中搜索.macro CacheLookup
,找到缓存查找的实现
.macro CacheLookup
LLookupStart$1:
// p1 = SEL, p16 = isa
ldr p11, [x16, #CACHE] // p11 = mask|buckets
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
and p10, p11, #0x0000ffffffffffff // p10 = buckets
and p12, p1, p11, LSR #48 // x12 = _cmd & mask
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
and p10, p11, #~0xf // p10 = buckets
and p11, p11, #0xf // p11 = maskShift
mov p12, #0xffff
lsr p11, p12, p11 // p11 = mask = 0xffff >> p11
and p12, p1, p11 // x12 = _cmd & mask
#else
#error Unsupported cache mask storage for ARM64.
#endif
add p12, p10, p12, LSL #(1+PTRSHIFT)
// p12 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
ldp p17, p9, [x12] // {imp, sel} = *bucket
1: cmp p9, p1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
CheckMiss $0 // miss if bucket->sel == 0
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
3: // wrap: p12 = first bucket, w11 = mask
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
add p12, p12, p11, LSR #(48 - (1+PTRSHIFT))
// p12 = buckets + (mask << 1+PTRSHIFT)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
add p12, p12, p11, LSL #(1+PTRSHIFT)
// p12 = buckets + (mask << 1+PTRSHIFT)
#else
#error Unsupported cache mask storage for ARM64.
#endif
// Clone scanning loop to miss instead of hang when cache is corrupt.
// The slow path may detect any corruption and halt later.
ldp p17, p9, [x12] // {imp, sel} = *bucket
1: cmp p9, p1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
CheckMiss $0 // miss if bucket->sel == 0
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
LLookupEnd$1:
LLookupRecover$1:
3: // double wrap
JumpMiss $0
.endmacro
-
ldr p11, [x16, #CACHE] // p11 = mask|buckets
平移16位获取Cache
; - 获取
buckets
和mask
; - 比较获取的
bucket
中sel
与objc_msgSend
的第二个参数的_cmd(即p1)
是否相等,如果相等调用CacheHit
,否则直接跳转至最后一个(查找方式永远是向前查找),继续循环,一直到CheckMiss
满足条件。
CheckMiss
为Normal
时,调用__objc_msgLookup_uncached
函数
.macro CheckMiss
// miss if bucket->sel == 0
.if $0 == GETIMP
cbz p9, LGetImpMiss
.elseif $0 == NORMAL
cbz p9, __objc_msgSend_uncached
.elseif $0 == LOOKUP
cbz p9, __objc_msgLookup_uncached
.else
.abort oops
.endif
.endmacro
JumpMiss
为Normal
时,调用__objc_msgLookup_uncached
函数
.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
__objc_msgLookup_uncached
MethodTableLookup
查询方法列表
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band p16 is the class to search
MethodTableLookup // 开始查询方法列表
TailCallFunctionPointer x17
END_ENTRY __objc_msgSend_uncached
MethodTableLookup
调用_lookUpImpOrForward
,慢速查找流程
.macro MethodTableLookup
// push frame
SignLR
stp fp, lr, [sp, #-16]!
mov fp, sp
// save parameter registers: x0..x8, q0..q7
sub sp, sp, #(10*8 + 8*16)
stp q0, q1, [sp, #(0*16)]
stp q2, q3, [sp, #(2*16)]
stp q4, q5, [sp, #(4*16)]
stp q6, q7, [sp, #(6*16)]
stp x0, x1, [sp, #(8*16+0*8)]
stp x2, x3, [sp, #(8*16+2*8)]
stp x4, x5, [sp, #(8*16+4*8)]
stp x6, x7, [sp, #(8*16+6*8)]
str x8, [sp, #(8*16+8*8)]
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
// receiver and selector already in x0 and x1
mov x2, x16
mov x3, #3
bl _lookUpImpOrForward //核心源码
// IMP in x0
mov x17, x0
// restore registers and return
ldp q0, q1, [sp, #(0*16)]
ldp q2, q3, [sp, #(2*16)]
ldp q4, q5, [sp, #(4*16)]
ldp q6, q7, [sp, #(6*16)]
ldp x0, x1, [sp, #(8*16+0*8)]
ldp x2, x3, [sp, #(8*16+2*8)]
ldp x4, x5, [sp, #(8*16+4*8)]
ldp x6, x7, [sp, #(8*16+6*8)]
ldr x8, [sp, #(8*16+8*8)]
mov sp, fp
ldp fp, lr, [sp], #16
AuthenticateLR
.endmacro
网友评论