OC方法调用,其实都是转换为objc_msgSend函数调用
objc_msgSend的执行流程可以分为3大阶段:
- 消息发送
- 动态方法解析
- 消息转发
流程图
objc_msgSend.pngobjc_msgSend执行流程 - 源码
消息发送
- objc-msg-arm64.s
-> ENTRY _objc_msgSend
-> LNilOrTagged
b.eq LReturnZero // nil check
b.ne LGetIsaDone
-> LGetIsaDone:
CacheLookup NORMAL, _objc_msgSend
-> .macro CacheLookup NORMAL
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
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
-> .macro CheckMiss
cbz p9, __objc_msgSend_uncached
-> STATIC_ENTRY __objc_msgSend_uncached
MethodTableLookup
-> .macro MethodTableLookup
bl _lookUpImpOrForward // jmp2 lookUpImpOrForward objc_runtime-new.mm
- objc_runtime-new.mm
消息发送(方法查找)
-> lookUpImpOrForward
-> getMethodNoSuper_nolock
-> search_method_list_inline
方列列表有序则:二分查找 findMethodInSortedMethodList
方法列表无序则:遍历若找到方法:
-> log_and_fill_cache -> cache_fill -> getCache { return &cls->cache; } -> insert
若没有找到方法:
-> 判断curClass = curClass->superclass 是否 nil
- 若curClass不为nil
->cache_getImp -> log_and_fill_cache -> getMethodNoSuper_nolock -> log_and_fill_cache ...- 若curClass为nil,则进入
动态方法解析
动态方法解析
imp = forward_imp; break 跳出循环,进行动态方法解析
-> resolveMethod_locked -> resolveInstanceMethod (resolveClassMethod)
->lookUpImpOrForward -> cache_getImp
- cache_getImp 有值
->done_nolock- cache_getImp 无值,重复消息发送阶段,直到curClass为nil,
imp = forward_imp = (IMP)_objc_msgForward_impcache; 进入消息转发
阶段
//消息发送 /动态方法解析/消息转发
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior) {
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
Class curClass;
IMP imp = nil;
// 动态方法解析调用,动态添加方法时执行
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
for (unsigned attempts = unreasonableClassCount();;) {
Method meth = getMethodNoSuper_nolock(curClass, sel); //查找方法
//若找到方法,jmp2 done
if (meth) {
imp = meth->imp;
goto done;
}
if (slowpath((curClass = curClass->superclass) == nil)) { //父类赋值当前类,并判断是否为nil,若为nil则跳出循环,执行动态方法解析,若已经解析过,则进行消息转发
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.");
}
// 查找父类缓存
imp = cache_getImp(curClass, sel);
if (slowpath(imp == forward_imp)) {
break;
}
//若父类缓存存在,jmp2 done
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
goto done;
}
}//end for
//动态方法解析
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
done:
log_and_fill_cache(cls, imp, sel, inst, curClass);
runtimeLock.unlock();
done_nolock:
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}
//方法查找
static method_t *getMethodNoSuper_nolock(Class cls, SEL sel){
//cls -> data() 获得class_rw_t 表
//cls->data()->methods() 获取method_array_t 二维数组{method_list_t,method_list_t }
auto const methods = cls->data()->methods();
for (auto mlists = methods.beginLists(),
end = methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list_inline(*mlists, sel);
if (m) return m;
}
}
static method_t * search_method_list_inline(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
if (fastpath(methodListIsFixedUp && methodListHasExpectedSize)) {
//二分查找
return findMethodInSortedMethodList(sel, mlist);
} else {
// 遍历
for (auto& meth : *mlist) {
if (meth.name == sel) return &meth;
}
}
#if DEBUG
// sanity-check negative results
if (mlist->isFixedUp()) {
for (auto& meth : *mlist) {
if (meth.name == sel) {
_objc_fatal("linear search worked when binary search did not");
}
}
}
#endif
return nil;
}
static method_t *
findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
ASSERT(list);
const method_t * const first = &list->first;
const method_t *base = first;
const method_t *probe;
uintptr_t keyValue = (uintptr_t)key;
uint32_t count;
for (count = list->count; count != 0; count >>= 1) {
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
if (keyValue == probeValue) {
// `probe` is a match.
// Rewind looking for the *first* occurrence of this value.
// This is required for correct category overrides.
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
probe--;
}
return (method_t *)probe;
}
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
//动态方法解析
static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
runtimeLock.unlock();
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
resolveInstanceMethod(inst, sel, cls);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
resolveClassMethod(inst, sel, cls);
if (!lookUpImpOrNil(inst, sel, cls)) {
resolveInstanceMethod(inst, sel, cls);
}
}
// chances are that calling the resolver have populated the cache
// so attempt using it
return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
SEL resolve_sel = @selector(resolveInstanceMethod:);
if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNil(inst, sel, cls);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
ASSERT(cls->isMetaClass());
if (!lookUpImpOrNil(inst, @selector(resolveClassMethod:), cls)) {
// Resolver not implemented.
return;
}
Class nonmeta;
{
mutex_locker_t lock(runtimeLock);
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// +initialize path should have realized nonmeta already
if (!nonmeta->isRealized()) {
_objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
nonmeta->nameForLogging(), nonmeta);
}
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNil(inst, sel, cls);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
消息转发
- objc-msg-arm64.s
-> STATIC_ENTRY __objc_msgForward_impcache
-> ENTRY __objc_msgForward
-> Core Foundation
-> __forwarding__(不开源)
网友评论