在Object-c中,OC方法的調(diào)用在編譯時(shí)都會(huì)轉(zhuǎn)化為對(duì)函數(shù)objc_msgSend的調(diào)用。
舉個(gè)例子
[AppDelegate alloc];
// ((AppDelegate *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("alloc"));
objc_msgSend(objc_getClass("AppDelegate"), sel_registerName("alloc"));
[view1 addSubview:view2];
// ((void (*)(id, SEL, UIView * _Nonnull))(void *)objc_msgSend)((id)view1, sel_registerName("addSubview:"), (UIView *)view2);
objc_msgSend(view1, sel_registerName("addSubview:"), view2);
本文章貼出的代碼出自 objc4-781/
下載地址下載地址
objc_msgSend函數(shù)
objc_msgSend函數(shù)是OC方法調(diào)用的核心,負(fù)責(zé)查找方法的實(shí)現(xiàn),并執(zhí)行方法函數(shù)。因調(diào)用頻率非常高,函數(shù)的內(nèi)部代碼是用匯編語(yǔ)言編寫,并且沒(méi)有涉及需要線程同步和鎖相關(guān)的代碼。
簡(jiǎn)單的宏定義
;定義一個(gè)名字叫做 ENTRY 的宏
.macro ENTRY /* name */
.text
.align 5
.globl $0
$0:
.endmacro
;定義一個(gè)名字叫做 END_ENTRY 的宏
.macro END_ENTRY /* name */
LExit$0:
.endmacro
_objc_msgSend
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
// p0是objc_msgSend的第一個(gè)參數(shù) obj,p0 - 0 結(jié)果放到cpsr寄存器里
cmp p0, #0 // nil check and tagged pointer check
// 如果支持 tagged_pointers
#if SUPPORT_TAGGED_POINTERS
// le 小于等于,如果第一個(gè)參數(shù)小于等于0 走 LNilOrTagged 流程
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
// eq 等于, 直接返回 0
b.eq LReturnZero
#endif
// isa 存儲(chǔ)到 p13寄存器,再存儲(chǔ)到p16寄存器
ldr p13, [x0] // p13 = isa
GetClassFromIsa_p16 p13 // p16 = class
LGetIsaDone:
// calls imp or objc_msgSend_uncached
// 查找緩存,調(diào)用方法
CacheLookup NORMAL, _objc_msgSend
#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 ;直接返回0
END_ENTRY _objc_msgSend
CacheLookup宏定義
.macro CacheLookup
// NORMAL and LOOKUP:
// - x0 contains the receiver
// - x1 contains the selector
// - x16 contains the isa
// - other registers are set as per calling conventions
LLookupStart$1:
// p1 = SEL, p16 = isa
ldr p11, [x16, #CACHE] // p11 = mask|buckets 通過(guò)x16存儲(chǔ)的isa 加上cache的地址偏移,或者cache的地址,存儲(chǔ)到 p11 寄存器
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
and p10, p11, #0x0000ffffffffffff // p10 = buckets 找到bucket 線性表
and p12, p1, p11, LSR #48 // x12 = _cmd & mask 哈希計(jì)算,得到哈希值
#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))
// 讀取 imp 和 sel
ldp p17, p9, [x12] // {imp, sel} = *bucket
// 對(duì)比 取到的 imp 和參數(shù)的imp
1: cmp p9, p1 // if (bucket->sel != _cmd)
// 如果不相等,跳轉(zhuǎn)到 CheckMiss
b.ne 2f // scan more
// 如果相等,調(diào)用或者返回
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
// 沒(méi)有直接找到方法緩存,且當(dāng)前bucket放了其他方法,需要處理哈希碰撞
CheckMiss $0 // miss if bucket->sel == 0
// 比較 p12 和 p10,
cmp p12, p10 // wrap if bucket == buckets
// 相等表示判斷到了哈希表的開(kāi)頭
b.eq 3f
// 讀取數(shù)據(jù)到p13 然后 x12 = x12 - BUCKET_SIZE;
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
// 當(dāng)前讀取到了 buckets 的開(kāi)頭 ,再?gòu)淖詈笠粋€(gè)開(kāi)始算
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
找到緩存的處理方式
// CacheHit: x17 = cached IMP, x12 = address of cached IMP, x1 = SEL, x16 = isa
.macro CacheHit
.if $0 == NORMAL
TailCallCachedImp x17, x12, x1, x16 // authenticate and call imp
.elseif $0 == GETIMP
mov p0, p17
cbz p0, 9f // don't ptrauth a nil imp
AuthAndResignAsIMP x0, x12, x1, x16 // authenticate imp and re-sign as IMP
9: ret // return IMP
.elseif $0 == LOOKUP
// No nil check for ptrauth: the caller would crash anyway when they
// jump to a nil IMP. We don't care if that jump also fails ptrauth.
AuthAndResignAsIMP x17, x12, x1, x16 // authenticate imp and re-sign as IMP
ret // return imp via x17
.else
.abort oops
.endif
.endmacro
.macro TailCallCachedImp
// $0 = cached imp, $1 = address of cached imp, $2 = SEL, $3 = isa
eor $1, $1, $2 // mix SEL into ptrauth modifier
eor $1, $1, $3 // mix isa into ptrauth modifier
brab $0, $1
.endmacro
找不到緩存的處理方式
.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
.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_msgSend_uncached
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
.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
查找方法
如果方法緩存里找不到方法,則會(huì)脫離匯編,使用高級(jí)語(yǔ)言進(jìn)行下一流程
lookUpImpOrForward
NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
// 進(jìn)行消息轉(zhuǎn)發(fā)的imp
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
// 如果類還沒(méi)有初始化,就不緩存
if (slowpath(!cls->isInitialized())) {
behavior |= LOOKUP_NOCACHE;
}
// 加鎖
runtimeLock.lock();
/*
防止創(chuàng)建 看起來(lái)像類但實(shí)際上不是類的二進(jìn)制Blob并進(jìn)行CFI攻擊。
要確保這是一個(gè)內(nèi)置于二進(jìn)制文件中或通過(guò)objc_duplicateClass,objc_initializeClassPair或objc_allocateClassPair合法注冊(cè)的類。
// TODO:此檢查在流程啟動(dòng)期間非常昂貴。
*/
checkIsKnownClass(cls);
//判斷是否初始化,如果沒(méi)有,需要先初始化
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
runtimeLock.assertLocked();
curClass = cls;
/*
在加鎖后, 以前會(huì)再次查找方法緩存, 但是在大多數(shù)情況下,證據(jù)表明大部分時(shí)間都未命中,因此會(huì)浪費(fèi)時(shí)間。
于是去掉了代碼
//唯一沒(méi)有執(zhí)行某種緩存查找的代碼路徑就是class_getInstanceMethod()。
*/
//unreasonableClassCount--表示類的迭代的上限
//(猜測(cè)這里遞歸的原因是attempts在第一次循環(huán)時(shí)作了減一操作,然后再次循環(huán)時(shí),仍在上限的范圍內(nèi),所以可以繼續(xù)遞歸)
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 {
// 在方法列表查找方法
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp(false);
goto done;
}
// 如果沒(méi)有父類
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
//--未找到方法實(shí)現(xiàn),方法解析器也不行,使用轉(zhuǎn)發(fā)
imp = forward_imp;
break;
}
}
// Halt if there is a cycle in the superclass chain.
//如果父類鏈中存在循環(huán),則停止
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache. 查找父類緩存
imp = cache_getImp(curClass, sel);
if (slowpath(imp == forward_imp)) {
// 如果在父類中找到了forward,則停止查找,且不緩存,首先調(diào)用此類的方法解析
break;
}
if (fastpath(imp)) {
//如果在父類中,找到了此方法,將其存儲(chǔ)到cache中
goto done;
}
}
// No implementation found. Try method resolver once.
//沒(méi)有找到方法實(shí)現(xiàn),嘗試一次方法解析
if (slowpath(behavior & LOOKUP_RESOLVER)) {
//動(dòng)態(tài)方法決議的控制條件,表示流程只走一次
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
//存儲(chǔ)到緩存
log_and_fill_cache(cls, imp, sel, inst, curClass);
}
done_unlock:
runtimeLock.unlock();
//如果不需要找了(LOOKUP_NIL),并且imp等于forward_imp,就返回nil。
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}
forward_imp的賦值
forward_imp是通過(guò)_objc_msgForward_impcache函數(shù)賦值的。全局搜索一下。發(fā)現(xiàn)這個(gè)函數(shù)是個(gè)匯編函數(shù),找到其實(shí)現(xiàn):
STATIC_ENTRY __objc_msgForward_impcache
// No stret specialization.
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
TailCallFunctionPointer x17
END_ENTRY __objc_msgForward
objc_defaultForwardHandler(id self, SEL sel)
{
_objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
"(no message forward handler is installed)",
class_isMetaClass(object_getClass(self)) ? '+' : '-',
object_getClassName(self), sel_getName(sel), self);
}
這就是我們?cè)谌粘i_(kāi)發(fā)中最常見(jiàn)的錯(cuò)誤:沒(méi)有實(shí)現(xiàn)函數(shù),運(yùn)行程序,崩潰時(shí)報(bào)的錯(cuò)誤提示:unrecognized selector sent to instance。
forward_imp的值就是未找到imp的函數(shù)實(shí)現(xiàn)
在方法列表里找方法
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
// fixme nil cls?
// fixme nil sel?
// 獲得方法列表,是一個(gè)二維數(shù)組,每一個(gè)元素存儲(chǔ)了分類或者類的方法
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;
}
return nil;
}
search_method_list_inline
ALWAYS_INLINE static method_t *search_method_list_inline(const method_list_t *mlist, SEL sel)
{
//方法列表是否排序過(guò)
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
// 如果找不到,測(cè)試是否出了問(wèn)題,并拋出異常
// 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;
}
二分法查找方法 findMethodInSortedMethodList
ALWAYS_INLINE 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;
}
填充方法緩存
log_and_fill_cache
static void log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
#if SUPPORT_MESSAGE_LOGGING
if (slowpath(objcMsgLogEnabled && implementer)) {
bool cacheIt = logMessageSend(implementer->isMetaClass(),
cls->nameForLogging(),
implementer->nameForLogging(),
sel);
if (!cacheIt) return;
}
#endif
cache_fill(cls, sel, imp, receiver);
}
cache_fill
void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
{
runtimeLock.assertLocked();
#if !DEBUG_TASK_THREADS
// Never cache before +initialize is done
if (cls->isInitialized()) {
cache_t *cache = getCache(cls);
#if CONFIG_USE_CACHE_LOCK
mutex_locker_t lock(cacheUpdateLock);
#endif
// 插入方法緩存
cache->insert(cls, sel, imp, receiver);
}
#else
_collecting_in_critical();
#endif
}
insert
ALWAYS_INLINE void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver)
{
#if CONFIG_USE_CACHE_LOCK
cacheUpdateLock.assertLocked();
#else
runtimeLock.assertLocked();
#endif
ASSERT(sel != 0 && cls->isInitialized());
// Use the cache as-is if it is less than 3/4 full
mask_t newOccupied = occupied() + 1;
unsigned oldCapacity = capacity(), capacity = oldCapacity;
// 如果是空表,需要去創(chuàng)建表
if (slowpath(isConstantEmptyCache())) {
// Cache is read-only. Replace it.
if (!capacity) capacity = INIT_CACHE_SIZE;
reallocate(oldCapacity, capacity, /* freeOld */false);
}
else if (fastpath(newOccupied + CACHE_END_MARKER <= capacity / 4 * 3)) {// 小于3/4
// Cache is less than 3/4 full. Use it as-is.
}
else {// 擴(kuò)容
capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
if (capacity > MAX_CACHE_SIZE) {
capacity = MAX_CACHE_SIZE;
}
reallocate(oldCapacity, capacity, true);
}
bucket_t *b = buckets();
mask_t m = capacity - 1;
mask_t begin = cache_hash(sel, m);
mask_t i = begin;
/*
掃描第一個(gè)未使用的插槽,然后將其插入。
保證有一個(gè)空插槽,因?yàn)槲覀儗⒄J(rèn)為調(diào)整為3/4就滿了。
*/
do {
if (fastpath(b[i].sel() == 0)) {
incrementOccupied();
// 插入數(shù)據(jù)
b[i].set<Atomic, Encoded>(sel, imp, cls);
return;
}
if (b[i].sel() == sel) { // 如果已經(jīng)緩存
// The entry was added to the cache by some other thread
// before we grabbed the cacheUpdateLock.
return;
}
} while (fastpath((i = cache_next(i, m)) != begin));
// 插入失敗,拋出異常
cache_t::bad_cache(receiver, (SEL)sel, cls);
}
動(dòng)態(tài)方法解析
resolveMethod_locked
static NEVER_INLINE IMP resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
runtimeLock.unlock();
if (! cls->isMetaClass()) {
// 如果不是元類直接調(diào)用resolveInstanceMethod方法
resolveInstanceMethod(inst, sel, cls);
}
else {
//調(diào)用元類的 resolveClassMethod: 和 resolveInstanceMethod: 方法
resolveClassMethod(inst, sel, cls);
if (!lookUpImpOrNil(inst, sel, cls)) {
// 調(diào)用找不到實(shí)例方法
resolveInstanceMethod(inst, sel, cls);
}
}
return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}
resolveInstanceMethod
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;
// 調(diào)用 resolveInstanceMethod 方法
bool resolved = msg(cls, resolve_sel, sel);
// 重新查找方法
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));
}
}
}
resolveClassMethod
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));
}
}
}
lookUpImpOrNil
static inline IMP
lookUpImpOrNil(id obj, SEL sel, Class cls, int behavior = 0)
{
return lookUpImpOrForward(obj, sel, cls, behavior | LOOKUP_CACHE | LOOKUP_NIL);
}
消息轉(zhuǎn)發(fā)
_objc_msgForward_impcache
STATIC_ENTRY __objc_msgForward_impcache
// No stret specialization.
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
TailCallFunctionPointer x17
END_ENTRY __objc_msgForward
_objc_forward_handler
#if !__OBJC2__
// Default forward handler (nil) goes to forward:: dispatch.
void *_objc_forward_handler = nil;
void *_objc_forward_stret_handler = nil;
#else
// Default forward handler halts the process.
__attribute__((noreturn, cold)) void
objc_defaultForwardHandler(id self, SEL sel)
{
_objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
"(no message forward handler is installed)",
class_isMetaClass(object_getClass(self)) ? '+' : '-',
object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
代碼跟讀到此為止,通過(guò)模擬崩潰可以得知
frame #10: 0x0000000108d4f610 CoreFoundation`+[NSObject(NSObject) doesNotRecognizeSelector:] + 132
frame #11: 0x0000000108d45032 CoreFoundation`___forwarding___ + 1489
frame #12: 0x0000000108d47068 CoreFoundation`__forwarding_prep_0___ + 120
* frame #13: 0x0000000108247b36 測(cè)試`main(argc=1, argv=0x00007ffee79b7cb0) at main.m:10:9
調(diào)用了一個(gè)不開(kāi)源的 ___forwarding___ 方法,
偽代碼如下
其實(shí)就是調(diào)用 forwardingTargetForSelector,methodSignatureForSelector,forwardInvacation 方法
int __forwarding__(void *frameStackPointer, int isStret) {
id receiver = *(id *)frameStackPointer;
SEL sel = *(SEL *)(frameStackPointer + 8);
const char *selName = sel_getName(sel);
Class receiverClass = object_getClass(receiver);
// 調(diào)用 forwardingTargetForSelector:
if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) {
id forwardingTarget = [receiver forwardingTargetForSelector:sel];
if (forwardingTarget && forwardingTarget != receiver) {
return objc_msgSend(forwardingTarget, sel, ...);
}
}
// 調(diào)用 methodSignatureForSelector 獲取方法簽名后再調(diào)用 forwardInvocation
if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:))) {
NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
if (methodSignature && class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) {
NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];
[receiver forwardInvocation:invocation];
void *returnValue = NULL;
[invocation getReturnValue:&value];
return returnValue;
}
}
if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:))) {
[receiver doesNotRecognizeSelector:sel];
}
// The point of no return.
kill(getpid(), 9);
}