在上一篇iOS底層探索之objc_msgSend流程——快速查找文章中,我們分析了快速查找流程,如果快速查不到,則需要進(jìn)入慢速查找流程,以下是慢速查找的分析過程
objc_msgSend慢速查找流程
在快速查找流程中,如果沒有找到方法實現(xiàn),無論是走到CheckMiss或者JumpMiss,最終會走到__objc_msgSend_uncached的匯編函數(shù)。找到源碼如下:
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
通過分析__objc_msgSend_uncached的核心是MethodTableLookup查找方法列表。而MethodTableLookup的實現(xiàn)的核心源碼為_lookUpImpOrForward。
通過斷點調(diào)試看是不是會走lookUpImpOrForward這個函數(shù),于是我們在那個調(diào)用方法時進(jìn)行斷點調(diào)試:
在調(diào)用方法時加一個斷點,執(zhí)行斷住,按住control + stepinto,進(jìn)入?yún)R編

在
_objc_msgSend_uncached加一個斷點,執(zhí)行斷住,按住control + stepinto,進(jìn)入?yún)R編
發(fā)現(xiàn)
lookUpImpOrForward并不是匯編實現(xiàn)而是C++實現(xiàn)。于是我們通過查找源碼,全局搜索lookUpImpOrForward。
lookUpImpOrForward分析
最終在objc-runtime-new.mm的文件中找到了源碼實現(xiàn),源碼如下:
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
//定義的消息轉(zhuǎn)發(fā)
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
//快速查找,如果找到則直接返回imp
//目的:防止多線程操作時,剛好調(diào)用函數(shù),此時緩存進(jìn)來了
// Optimistic cache lookup
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
//加鎖,目的是保證讀取的線程安全
runtimeLock.lock();
//判斷是否是一個已知類:判斷當(dāng)前類是否是已經(jīng)被認(rèn)可的類,即已經(jīng)加載的類
checkIsKnownClass(cls);
//判斷類是否實現(xiàn),如果沒有,需要先實現(xiàn),此時的目的是為了確定父類鏈,方法后續(xù)的循環(huán)。
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
//判斷是否初始化,如果沒有,需要先初始化
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
runtimeLock.assertLocked();
curClass = cls;
//--查找類的緩存
//unreasonableClassCount--表示類的迭代的上限
//(猜測這里遞歸的原因是attempts在第一次循環(huán)時作了減一操作,然后再次循環(huán)時,仍在上限的范圍內(nèi),所以可以繼續(xù)遞歸)
for (unsigned attempts = unreasonableClassCount();;) {
//--當(dāng)前類方法列表(采用二分查找算法),如果找到,則返回,將方法緩存到cache中。
// curClass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp;
goto done;
}
//當(dāng)前類 - 當(dāng)前類的父類,并判斷父類是否nil
if (slowpath((curClass = curClass->superclass) == nil)) {
//--未找到方法實現(xiàn),方法解析器也不行,使用轉(zhuǎn)發(fā)
imp = forward_imp;
break;
}
//如果父類鏈中存在循環(huán),則停止
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
//objc_msgSend->二分查找->cache_fill寫入緩存->objc_msgSend
// --父類緩存
imp = cache_getImp(curClass, sel);
if (slowpath(imp == forward_imp)) {
// 如果在父類中找到了forward,則停止查找,且不緩存,首先調(diào)用此類的方法解析器
break;
}
if (fastpath(imp)) {
//如果在父類中,找到了此方法,將其存儲到cache中
goto done;
}
}
// No implementation found. Try method resolver once.
//沒有找到方法實現(xiàn),嘗試一次方法解析
if (slowpath(behavior & LOOKUP_RESOLVER)) {
//動態(tài)方法決議的控制條件,表示流程只走一次
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;
}
forward_imp的賦值
forward_imp是通過_objc_msgForward_impcache函數(shù)賦值的。全局搜索一下。發(fā)現(xiàn)這個函數(shù)是個匯編函數(shù),找到其實現(xiàn):
//入口
STATIC_ENTRY __objc_msgForward_impcache
//跳轉(zhuǎn)__objc_msgForward
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
//__objc_msgForward入口
ENTRY __objc_msgForward
//賦值并且返回x17的地址。
adrp x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
TailCallFunctionPointer x17
END_ENTRY __objc_msgForward
根據(jù)源碼分析重點__objc_forward_handler
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;
看著objc_defaultForwardHandler很眼熟,這就是我們在日常開發(fā)中最常見的錯誤:沒有實現(xiàn)函數(shù),運行程序,崩潰時報的錯誤提示。
forward_imp的值就是未找到imp的函數(shù)實現(xiàn)
cache緩存中進(jìn)行查找
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
這里的目的是防止多線程操作時,剛好調(diào)用了函數(shù),此時緩存進(jìn)來了,_cache_getImp快速查找,如果找到直接返回imp。
STATIC_ENTRY _cache_getImp
GetClassFromIsa_p16 p0
CacheLookup GETIMP, _cache_getImp
LGetImpMiss:
mov p0, #0
ret
END_ENTRY _cache_getImp
檢查類是否合法
checkIsKnownClass(cls);
確保cls在已知類列表中,避免傳入一個非類的二進(jìn)制文件,進(jìn)行CFI攻擊。
確定當(dāng)前類的繼承鏈和isa的繼承鏈
確定當(dāng)前類的父類,并且遞歸執(zhí)行,最終確定類的繼承鏈;
確定元類,并把元類的繼承鏈也遞歸確定下來;
在initlize時,執(zhí)行addSubClass(),將子類也存入父類中,從而實現(xiàn)了雙向鏈表
static Class
realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
{
return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}
static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
lock.assertLocked();
if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
// Non-Swift class. Realize it now with the lock still held.
// fixme wrong in the future for objc subclasses of swift classes
realizeClassWithoutSwift(cls, nil);
if (!leaveLocked) lock.unlock();
} else {
// Swift class. We need to drop locks and call the Swift
// runtime to initialize it.
lock.unlock();
cls = realizeSwiftClass(cls);
ASSERT(cls->isRealized()); // callback must have provoked realization
if (leaveLocked) lock.lock();
}
return cls;
}
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) return cls;
ASSERT(cls == remapClass(cls));
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
cls->setInstancesRequireRawIsa();
} else {
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->name, "OS_object"))
{
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
#endif
cls->superclass = supercls;
cls->initClassIsa(metacls);
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
cls->setInstanceSize(ro->instanceSize);
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
methodizeClass(cls, previously);
//cls->雙向鏈表結(jié)構(gòu)
return cls;
}
進(jìn)入for死循環(huán)按照類的繼承鏈或者元類的繼承鏈的順序查找。
當(dāng)前cls的方法列表中使用二分查找算法查找方法,如果找到,則進(jìn)入cache寫入流程,并返回imp,如果沒有找到,則返回nil。
當(dāng)前cls被賦值為父類,如果父類等于nil,則imp = 消息轉(zhuǎn)發(fā),并終止遞歸,進(jìn)入判斷是否執(zhí)行過動態(tài)方法解析:如果沒有,則執(zhí)行動態(tài)方法解析;如果執(zhí)行過一次動態(tài)方法解析,則走到消息轉(zhuǎn)發(fā)流程。
如果父類鏈中存在循環(huán),則報錯,終止循環(huán)。父類緩存中查找方法, 如果未找到,則直接返回nil,繼續(xù)循環(huán)查找; 如果找到,則直接返回imp,執(zhí)行cache寫入流程。
getMethodNoSuper_nolock 方法
getMethodNoSuper_nolock查找本類中的方法列表
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
// fixme nil cls?
// fixme nil sel?
auto const methods = cls->data()->methods();
for (auto mlists = methods.beginLists(),
end = methods.endLists();
mlists != end;
++mlists)
{
// <rdar://problem/46904873> getMethodNoSuper_nolock is the hottest
// caller of search_method_list, inlining it turns
// getMethodNoSuper_nolock into a frame-less function and eliminates
// any store from this codepath.
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)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
if (fastpath(methodListIsFixedUp && methodListHasExpectedSize)) {
return findMethodInSortedMethodList(sel, mlist);
} else {
// Linear search of unsorted method list
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;
}
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;
//base相當(dāng)于low,count是max,probe是middle,這就是二分
for (count = list->count; count != 0; count >>= 1) {
//從首地址+下標(biāo) --> 移動到中間位置(count >> 1 左移1位即 count/2 = 4)
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
//如果查找的key的keyvalue等于中間位置(probe)的probeValue,則直接返回中間位置
if (keyValue == probeValue) {
// -- while 平移 -- 排除分類重名方法
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
//排除分類重名方法(方法的存儲是先存儲類方法,在存儲分類---按照先進(jìn)后出的原則,分類方法最先出,而我們要取的類方法,所以需要先排除分類方法)
//如果是兩個分類,就看誰先進(jìn)行加載
probe--;
}
return (method_t *)probe;
}
//如果keyValue 大于 probeValue,就往probe即中間位置的右邊查找
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
進(jìn)入動態(tài)方法決議
上面for循環(huán)跳出來之后,說明沒有找到imp,會進(jìn)入動態(tài)方法決議。
if (slowpath(behavior & LOOKUP_RESOLVER)) {
//動態(tài)方法決議的控制條件,表示流程只走一次
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
查找到imp結(jié)果
當(dāng)我們找到imp時,就會直接進(jìn)入done流程,將cls的sel和imp寫入緩存中(便于下次同樣的方法,可以在cache匯編快速查詢到)。
done結(jié)束后,我們會進(jìn)入done_nolock流程, 如果imp是forward_imp,就返回nil,否則,返回正常的imp。
done:
// 從將sel和imp寫入cls的緩存
log_and_fill_cache(cls, imp, sel, inst, curClass);
// 運行時解鎖
runtimeLock.unlock();
done_nolock:
//如果不需要找了(LOOKUP_NIL),并且imp等于forward_imp,就返回nil。
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
// 返回當(dāng)前獲取的imp
return imp;
總結(jié)
當(dāng)在objc_msgSend緩存中沒有找到方法,就會來到CheckMiss -> __objc_msgSend_uncached -> MethodTableLookup -> lookUpImpOrForward進(jìn)行慢速查找流程。lookUpImpOrForward中先在本類中查找方法,如果沒有找到就會循環(huán)的去父類當(dāng)中找,直到找到NSObject中,如果沒有找到,就會進(jìn)行動態(tài)方法決議,動態(tài)方法不處理,則進(jìn)入動態(tài)消息轉(zhuǎn)發(fā)階段。此時可以在動態(tài)消息轉(zhuǎn)發(fā)階段做一下處理,如果還不進(jìn)行處理,最后崩潰報錯unrecognized selector sent to instance ...。