Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写
简介
-
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
-
main.m -> main.cpp
-
main.m 调用了一个函数
-
c++ 文件中的执行
-
结论是:
[person persontest]的本质是 调用了objc_msgSend(person,sel_registerName("persontest")) -
sel_registerName("persontest")是runtime的C语言函数等价于@selector(personTest) -
所以
objc_msgSend(person,@selector(personTest)) -
类方法的调用其实也是一样的
objc_msgSend简介
- OC中的方法调用,其实都是转换为objc_msgSend函数的调用
- OC方法调用:给消息接受者发送消息
MJPerson *person = [[MJPerson alloc] init]; [person personTest]; // objc_msgSend(person, @selector(personTest)); // 消息接收者(receiver):person // 消息名称:personTest [MJPerson initialize]; // objc_msgSend([MJPerson class], @selector(initialize)); // 消息接收者(receiver):[MJPerson class] // 消息名称:initialize - objc_msgSend的执行流程可以分为3大阶段
- 消息发送
- 动态方法解析
- 消息转发
objc_msgSend消息发送 源码查看
- objc_msgSend的汇编源码具体实现
//进入_objc_msgSend
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
//p0: p0寄存器的值 p0:存放objc_msgSend的第一个参数 是Self
//cmp:比较指令
//这里是判断接受者是不是空
cmp p0, #0 // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
//如果支持 SUPPORT_TAGGED_POINTERS。判断上面的比较结果,是否 ≤ 0,是则跳转到 LNilOrTagged 进行处理。因为在 arm64 下,当为 Tagged pointer 时,最高位是 1,作为有符号数,< 0。
//不支持的话,则判断比较结果是否为 0。如果为 0,则跳转到 LReturnZero 进行 nil 的处理。
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
b.eq LReturnZero
#endif
// 以上都不成立
// 在 arm64 下,p0 和 x0 是等价的 x0 = self 因为 isa 是
//objc_object 中只有一个成员 isa, 因此取出指针指向的内容,也就获取到了 isa 的值。
// p13 = isa
ldr p13, [x0]
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:
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
- 缓存查找 简化一下
.macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant
//x16寄存器的内容放到x15 就是把 class的地址保存一份
mov x15, x16 // stash the original isa
LLookupStart\Function:
// p1 = SEL, p16 = isa
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
// 将 cache_t 的地址放入 p10 cache_t
ldr p10, [x16, #CACHE] // p10 = mask|buckets
// 将 cache_t 的地址放入 p11 逻辑右移,高地址向低地址移动48位剩下的16位就是mask
lsr p11, p10, #48 // p11 = mask
//与运行 p10与上掩码得到buckets 存放到p10
and p10, p10, #0xffffffffffff // p10 = buckets
//w1 其实就是 x1 方法的第二个参数 SEL cmd:SEL & mask 求出对应的index
and w12, w1, w11 // x12 = _cmd & mask
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
ldr p11, [x16, #CACHE] // p11 = mask|buckets
#if CONFIG_USE_PREOPT_CACHES
#if __has_feature(ptrauth_calls)
tbnz p11, #0, LLookupPreopt\Function
and p10, p11, #0x0000ffffffffffff // p10 = buckets
#else
and p10, p11, #0x0000fffffffffffe // p10 = buckets
tbnz p11, #0, LLookupPreopt\Function
#endif
eor p12, p1, p1, LSR #7
and p12, p12, p11, LSR #48 // x12 = (_cmd ^ (_cmd >> 7)) & mask
#else
and p10, p11, #0x0000ffffffffffff // p10 = buckets
and p12, p1, p11, LSR #48 // x12 = _cmd & mask
#endif // CONFIG_USE_PREOPT_CACHES
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
ldr p11, [x16, #CACHE] // p11 = mask|buckets
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
//上面的判断是在不同的架构下的判断运算规则但是整体不变的是
//p10 里面放了 buckets 散列表
//p12 里面放了 _cmd & mask = index
//p13 里面就放了 bucket_t 的地址
add p13, p10, p12, LSL #(1+PTRSHIFT)
// p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
// do {
// 从定位到的表项地址中,取出 2 个 8 字节数据放到 p17, p9 中。其中 p17 里是 imp,p9 里是 sel。
1: ldp p17, p9, [x13], #-BUCKET_SIZE // {imp, sel} = *bucket--
// 比较缓存中的 sel 和传入的 _cmd
cmp p9, p1 // if (sel != _cmd) {
// 不相等,跳转到 3 ne = not equal
b.ne 3f // scan more
// 命中缓存,调用 imp // } else {
2: CacheHit \Mode // hit: call or return imp
// }
// 检查 p9 中的 sel 是否为空,若为空,则跳转到 __objc_msgSend_uncached,再进行缓存未命中的查找
3: cbz p9, \MissLabelDynamic // if (sel == 0) goto Miss;
// 比较取到的缓存项和缓存表地址是否一致,也就是是否是第一项
cmp p13, p10 // } while (bucket >= buckets)
//结果如果大于或等于成立就跳到1
b.hs 1b
.endmacro
- 缓存中找到返回 imp方法的地址
- 因为传参第一个参数是normal
- x17 = 缓存 imp
- x10 = 查找到的缓存项地址
- x1 = sel
- x16 = class
- 缓存中没有找到方法则 调用MethodTableLookup
.macro MethodTableLookup
//保存寄存器
SAVE_REGS MSGSEND
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
// receiver and selector already in x0 and x1
//x2 保存了 cls
mov x2, x16
// LOOKUP_INITIALIZE = 1, LOOKUP_RESOLVER = 2, 两者或运算 = 3
mov x3, #3
// 调用 _lookUpImpOrForward 进行查找,最后查找到的 imp 放到 x0 中
bl _lookUpImpOrForward
// IMP in x0
// 将 imp 放到 x17
mov x17, x0
// 恢复寄存器
RESTORE_REGS MSGSEND
.endmacro
- 大佬文章 大佬文章可参考
- _lookUpImaOrForward 汇编方法去掉下划线才能在C语言中找到
NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
if (slowpath(!cls->isInitialized())) {
behavior |= LOOKUP_NOCACHE;
}
runtimeLock.lock();
checkIsKnownClass(cls);
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
runtimeLock.assertLocked();
curClass = cls;
for (unsigned attempts = unreasonableClassCount();;) {
if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
#if CONFIG_USE_PREOPT_CACHES
imp = cache_getImp(curClass, sel);
if (imp) goto done_unlock;
curClass = curClass->cache.preoptFallbackClass();
#endif
} else {
// curClass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp(false);
goto done;
}
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = forward_imp;
break;
}
}
// Halt if there is a cycle in the superclass chain.
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (slowpath(imp == forward_imp)) {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
goto done;
}
}
// No implementation found. Try method resolver once.
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
done:
if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {
#if CONFIG_USE_PREOPT_CACHES
while (cls->cache.isConstantOptimizedCache(/* strict */true)) {
cls = cls->cache.preoptFallbackClass();
}
#endif
log_and_fill_cache(cls, imp, sel, inst, curClass);
}
done_unlock:
runtimeLock.unlock();
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}
-
具体解释请参考这位大佬的文章 因为我用的源码是818.2他的用的是718.2所以有点出入。 参考文章
-
得出以下流程:
- 1.自己的cache ->
- 2.自己的methods ->
- 3.父亲的cache ->
- 4.父亲的methods ->
- 5.父亲的父亲的cache ->
- 6.父亲的父亲的methods ->
- 7.nil -> resolveMethod_locked(动态方法解析)
- 如果是从class_rw_t中查找方法
- 已经排序的,二分查找
- 没有排序的,遍历查找
- receiver通过isa指针找到receiverClass
- receiverClass通过superclass指针找到superClass