在之前寫(xiě)過(guò)一篇關(guān)于objc_msgSend的流程分析,而它就是方法的快速查找;那么本文將介紹一下方法的慢速查找流程。
在objc_msgSend的匯編代碼中,當(dāng)查找isa完畢之后,他會(huì)跳轉(zhuǎn)到CacheLookup的匯編代碼中執(zhí)行,當(dāng)系統(tǒng)在緩存中沒(méi)有找到相應(yīng)的方法,他就會(huì)繼續(xù)跳轉(zhuǎn)到CheckMiss的匯編代碼中。我們來(lái)看一下CheckMiss的源碼:
.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
當(dāng)系統(tǒng)走到這個(gè)過(guò)程中,會(huì)繼續(xù)執(zhí)行NORMAL中的cbz p9, __objc_msgSend_uncached這一行代碼,也就是說(shuō),我們就可以以這個(gè)方法為入口去探索慢速查找的過(guò)程。
我們進(jìn)行全局搜索__objc_msgSend_uncached這個(gè),找到了MethodTableLookup,這個(gè)是一個(gè)方法列表,繼續(xù)搜索這個(gè)方法,我們就只能查找到一個(gè)跳轉(zhuǎn)的bl _lookUpImpOrForward,之后我們就無(wú)法從找到這個(gè)方法的內(nèi)容。
那么按照我們往常的套路,我們把下劃線去掉,經(jīng)過(guò)搜索lookUpImpOrForward這個(gè)方法,找到了很多。
那么我們有沒(méi)有別的途徑去找到這個(gè)方法呢?
我們創(chuàng)建一個(gè)工程,在工程中創(chuàng)建一個(gè)類,在類中創(chuàng)建一個(gè)方法,在調(diào)用方法前打上斷點(diǎn):

把這個(gè)模式打開(kāi),可可以看到匯編中我們打上斷點(diǎn)的方法處:

在方法下面的objc_msgSend處打上斷點(diǎn):


就進(jìn)入了objc_msgSend方法中去了:

繼續(xù)往下滑,就會(huì)看到下面圖的樣子,在_objc_msgSend_uncached處打上斷點(diǎn),繼續(xù)按住control+


接著就會(huì)跳轉(zhuǎn)到_objc_msgSend_uncached方法中去,就能看到lookUpImpOrForward at objc-runtime-new.mm:6099這個(gè)信息。

下面繼續(xù)描述一下方法的一些基本常識(shí):
在一個(gè)類中,實(shí)例方法存儲(chǔ)在類中,類方法存儲(chǔ)在元類中;
那么當(dāng)我們用調(diào)用類方法的方式去調(diào)用實(shí)例方法,它也會(huì)執(zhí)行成功;那為什么用類方法的方式去調(diào)用實(shí)例方法,它也會(huì)成功呢?
答案在一張圖中:

在isa的走位中,根元類最后也會(huì)走到NSObject,因此,我們可以用調(diào)用類方法的方式去調(diào)用實(shí)例方法。
下面繼續(xù)探索慢速查找流程:
由于在上面我們找到了lookUpImpOrForward這個(gè)方法入口,那么我們就去看看這個(gè)方法的實(shí)現(xiàn)代碼:
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 (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
runtimeLock.lock();
checkIsKnownClass(cls);
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
}
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
runtimeLock.assertLocked();
curClass = cls;
for (unsigned attempts = unreasonableClassCount();;) {
// curClass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp;
goto done;
}
if (slowpath((curClass = curClass->superclass) == nil)) {
imp = forward_imp;
break;
}
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (slowpath(imp == forward_imp)) {
break;
}
if (fastpath(imp)) {
goto done;
}
}
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;
}
在main函數(shù)中調(diào)用一個(gè)方法,在方法調(diào)用處打上斷點(diǎn),在執(zhí)行程序之后,在if (fastpath(behavior & LOOKUP_CACHE)) {處打上斷點(diǎn),我們點(diǎn)擊下一步,程序就會(huì)跳轉(zhuǎn)到這一步上去;

我們發(fā)現(xiàn),快速查找會(huì)查找緩存中的方法,而慢速查找,它還要查找cache_getImp,那這是為什么呢?
答案是因?yàn)橐苊?code>多線程的影響,當(dāng)你程序在調(diào)用時(shí),線程可能偷偷摸摸的緩存了一次。
我們繼續(xù)往下看源碼,在下面有兩個(gè)if slowpath判斷,我們從上面的if (slowpath(!cls->isRealized()))的判斷內(nèi)去看它的實(shí)現(xiàn):
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
它里面存在一個(gè)方法realizeClassMaybeSwiftAndLeaveLocked,點(diǎn)擊方法進(jìn)去,它返回的也是一個(gè)方法:realizeClassMaybeSwiftMaybeRelock,繼續(xù)點(diǎn)擊,在realizeClassMaybeSwiftMaybeRelock方法中,有一個(gè)關(guān)鍵的方法:realizeClassWithoutSwift,點(diǎn)擊進(jìn)去,里面的代碼很多,貼出一部分源碼:
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) return cls;
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
在這個(gè)方法中,首先讀cls的data數(shù)據(jù),對(duì)rw,ro進(jìn)行賦值,然后就有了數(shù)據(jù);在有了數(shù)據(jù)之后,我們繼續(xù)讀下一塊代碼:
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);
里面有一個(gè)supercls和metacls兩個(gè)遞歸實(shí)現(xiàn),利用realizeClassWithoutSwift,把整個(gè)繼承鏈遞歸下來(lái),這符合那副經(jīng)典的走位圖。繼續(xù)看下一塊代碼:
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
methodizeClass(cls, previously);
return cls;
在這一塊中,有對(duì)cls->superclass和cls->initClassIsa進(jìn)行賦值,然后在下面會(huì)對(duì)supercls進(jìn)行判斷,分析這個(gè)if判斷,它是一個(gè)雙向鏈表結(jié)構(gòu),結(jié)下來(lái)的methodizeClass是對(duì)所有列表和屬性都會(huì)添加到rwe當(dāng)中去。
看完realizeClassMaybeSwiftAndLeaveLocked的大部分內(nèi)容之后,我們繼續(xù)往下看另一個(gè)slowpath中的initializeAndLeaveLocked方法;繼續(xù)點(diǎn)擊方法進(jìn)去,最終走到initializeAndMaybeRelock方法,在這個(gè)方法中最重要的一行代碼是調(diào)用的initializeNonMetaClass這個(gè)方法,這個(gè)方法,貼出一塊代碼:
Class supercls;
bool reallyInitialize = NO;
initialize cls.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
localWillInitializeFuncs.initFrom(willInitializeFuncs);
}
}
if (reallyInitialize) {
_setThisThreadIsInitializingClass(cls);
if (MultithreadedForkChild) {
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
for (auto callback : localWillInitializeFuncs)
callback.f(callback.context, cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
objc_thread_self(), cls->nameForLogging());
}
上面的一塊代碼中,作用很明顯,層層遞歸初始化所有方法的類;
知道這個(gè)作用之后,其他代碼也就不那么重要了,回到lookUpImpOrForward方法,繼續(xù)往下看源碼,它的返回值是imp,既然是返回imp,如果它找不到imp怎么辦呢?
那么我們就去看看imp賦值的地方在哪里,他的重點(diǎn)在for (unsigned attempts = unreasonableClassCount();;)for循環(huán)中,代碼在上面已經(jīng)貼出來(lái)了。我們?nèi)パ芯窟@一段代碼:
在代碼中有一個(gè)方法getMethodNoSuper_nolock獲取cls中的所有方法。
我們來(lái)看一下它的源碼:
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;
}
可以看到cls->data()->methods(),這個(gè)data()就是之前加載過(guò)來(lái)的ro,rw這些東西。
探索到這里,就涉及到一個(gè)算法,所有的方法都在method_list中,那么系統(tǒng)如何去查找方法呢?
答案:二分查找,二分查以中間點(diǎn)為界限,將精確的值確定在某個(gè)范圍內(nèi),經(jīng)過(guò)不斷縮小范圍,查找正確的內(nèi)容;
那么是如何知道系統(tǒng)是通過(guò)二分查找去找到對(duì)應(yīng)的方法呢?
在getMethodNoSuper_nolock方法中有一個(gè)對(duì)method_t類型的*m賦值的search_method_list_inline函數(shù),我們?cè)谶@個(gè)函數(shù)中有一個(gè)findMethodInSortedMethodList方法,下面附上這個(gè)方法的源碼:
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;
}
在for循環(huán)遍歷所有方法中,probe的賦值中有一個(gè)count >> 1位移操作,其作用是減少二分之一;
例如:8的二進(jìn)制為1000; 8>>1 100 = 4;
通過(guò)上面的代碼,我們可以了解到系統(tǒng)查找方法是使用二分查找來(lái)尋找內(nèi)容。
我們可以在控制臺(tái)中打印list中的內(nèi)容,就可以打印該類中的所有方法。
那么在上面的代碼中,probe--又是怎么理解呢?
它其實(shí)是來(lái)判斷分類重名的作用;
我們來(lái)探索一下:
首先創(chuàng)建一個(gè)Person,里面創(chuàng)建一個(gè)方法,在對(duì)Person創(chuàng)建一個(gè)分類。分類中創(chuàng)建一個(gè)相同的方法,在main函數(shù)中調(diào)用,得到的結(jié)果是打印的是分類方法。這邊就不貼代碼了,實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單。
當(dāng)系統(tǒng)沒(méi)有找到對(duì)應(yīng)的方法之后,它就會(huì)執(zhí)行goto done;代碼:
done:
log_and_fill_cache(cls, imp, sel, inst, curClass);
runtimeLock.unlock();
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);
}
那么在這個(gè)代碼中,它會(huì)對(duì)方法進(jìn)行緩存,以方便下次快速查找。
那么整個(gè)流程就是:objc_msgSend -> 二分查找自己 -> cache_fill ->objc_msgSend形成一個(gè)閉環(huán)。
如果在自己中沒(méi)有找到方法,那么它就會(huì)去父類去找,在父類中快速查找沒(méi)找到,他就會(huì)執(zhí)行cache_getImp方法,在經(jīng)過(guò)搜索這個(gè)方法,沒(méi)有找到,那么可以肯定的是,它存在匯編代碼中(只要與緩存相關(guān)的,一般都在匯編代碼中):
STATIC_ENTRY _cache_getImp
GetClassFromIsa_p16 p0
CacheLookup GETIMP, _cache_getImp
那么它又會(huì)執(zhí)行CacheLookup方法,接著又會(huì)跑到lookUpImpOrForward方法中查找。
注意事項(xiàng):當(dāng)在父類中快速查找中沒(méi)有找到方法,那么它不會(huì)繼續(xù)遞歸進(jìn)行慢速查找,而是繼續(xù)找父類的父類(NSObject)的緩存中進(jìn)行快速查找,當(dāng)在NSObject的緩存中沒(méi)有找到,那么繼續(xù)找NSObject的父類nil的緩存,進(jìn)入一個(gè)死循環(huán)過(guò)程。
那為什么在父類中沒(méi)有慢速查找過(guò)程呢?
由于imp的獲取在cache_getImp中,通過(guò)全局搜索,它存在匯編當(dāng)中,附上代碼:
STATIC_ENTRY _cache_getImp
GetClassFromIsa_p16 p0
CacheLookup GETIMP, _cache_getImp
可以看到它的CacheLookup是GETIMP類型;因此,在CacheLookup的匯編代碼最后會(huì)在JumpMiss中,附上他的代碼:
.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
可以看到,只有當(dāng)CacheLookup為NORMAL時(shí),才會(huì)進(jìn)行慢速查找。
而LGetImpMiss的代碼中,它最后只是進(jìn)行return返回。
那么如果在父類中還是沒(méi)有找到,那么在之前就會(huì)對(duì)(curClass = curClass->superclass) == nil)會(huì)判斷,將imp賦值為forward_imp,之后會(huì)執(zhí)行這一句判斷imp == forward_imp,相等的話,就會(huì)遞歸出去,跳出循環(huán)。
那么這個(gè)forward_imp又是什么東西呢?
在上面的代碼中,forward_imp是通過(guò)_objc_msgForward_impcache方法進(jìn)行賦值的,我們?nèi)タ纯催@個(gè)方法,經(jīng)過(guò)全局搜索,它存在匯編中,代碼我就不貼出來(lái)的,直接寫(xiě)出關(guān)鍵方法__objc_forward_handler,搜索_objc_forward_handler,就得到一個(gè)很有意思的東西:
__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);
}
上面的代碼,相信很多iOS開(kāi)發(fā)者都見(jiàn)過(guò)。當(dāng)方法找不到時(shí),就會(huì)顯示這個(gè)錯(cuò)誤。
那么在報(bào)錯(cuò)之前,它還會(huì)走到下面這部分代碼中:
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
那么我們就去看看這部分代碼中的內(nèi)容,首先點(diǎn)擊resolveMethod_locked,他是一個(gè)動(dòng)態(tài)方法決議;動(dòng)態(tài)方法決議其實(shí)是系統(tǒng)在沒(méi)有找到方法時(shí),它給你一個(gè)處理過(guò)程,處理完之后,它再繼續(xù)去查找,只要能找到方法所需要的內(nèi)容,就不會(huì)報(bào)錯(cuò),如果沒(méi)有,則報(bào)出錯(cuò)誤。
下面去看看它的源碼:
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);
}
在上面一段代碼中,重要的就在那個(gè)if判斷中,我們?nèi)タ纯?code>resolveInstanceMethod方法,請(qǐng)看源碼:
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));
}
}
}
這段源碼作用就是只要能找到這個(gè)方法的實(shí)現(xiàn),那么它就會(huì)去執(zhí)行這個(gè)方法,動(dòng)態(tài)方法協(xié)議其實(shí)是蘋(píng)果給我們的一個(gè)機(jī)會(huì),在快速和慢速都沒(méi)找到相應(yīng)的方法,那么它就給你一個(gè)去實(shí)現(xiàn)這個(gè)方法的機(jī)會(huì),只要系統(tǒng)找到了這個(gè)方法,那么他就能執(zhí)行成功。
下面我們用代碼來(lái)試一下:
在聲明文件中創(chuàng)建一個(gè)方法,在實(shí)現(xiàn)文件中不去寫(xiě)這個(gè)方法內(nèi)容,那么我們?cè)趍ain函數(shù)中執(zhí)行,它會(huì)報(bào)錯(cuò),那么我們用另一種方式去寫(xiě),就不會(huì)報(bào)錯(cuò)了:
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(say666)) {
NSLog(@"%@ 來(lái)了",NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(self, @selector(sayMaster));
Method sayMMethod = class_getInstanceMethod(self, @selector(sayMaster));
const char *type = method_getTypeEncoding(sayMMethod);
return class_addMethod(self, sel, imp, type);
}
return [super resolveInstanceMethod:sel];
}
上面的代碼是實(shí)例方法的動(dòng)態(tài)決議,那么類方法的動(dòng)態(tài)決議該如何實(shí)現(xiàn)呢?
類方法存在元類中,那么我們?cè)谔砑訉?duì)象時(shí),需要將類對(duì)象替換成元類對(duì)象,而元類一層一層往上走到最后,它最后走到的也是NSObject中,因此,我們可以在實(shí)例方法中利用if去進(jìn)行判斷,將類方法的動(dòng)態(tài)決議也寫(xiě)在實(shí)例方法中,請(qǐng)看代碼:
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"%@ 來(lái)了",NSStringFromSelector(sel));
if (sel == @selector(say666)) {
NSLog(@"%@ 來(lái)了",NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(self, @selector(sayMaster));
Method sayMMethod = class_getInstanceMethod(self, @selector(sayMaster));
const char *type = method_getTypeEncoding(sayMMethod);
return class_addMethod(self, sel, imp, type);
}
else if (sel == @selector(sayNB)) {
IMP imp = class_getMethodImplementation(objc_getMetaClass("Person"), @selector(lgClassMethod));
Method sayMMethod = class_getInstanceMethod(objc_getMetaClass("Person"), @selector(lgClassMethod));
const char *type = method_getTypeEncoding(sayMMethod);
return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);
}
return NO;
}
在上面的代碼中,say666是實(shí)例方法,sayNB是類方法,在代碼中,區(qū)別很明顯,類方法的對(duì)象是是要獲得該類的元類。
上面的代碼就是我們對(duì)中間層的處理,當(dāng)系統(tǒng)沒(méi)有在類中找到這個(gè)方法,那么就會(huì)啟動(dòng)動(dòng)態(tài)決議,給我們一個(gè)處理的過(guò)程,處理完之后,再重新去執(zhí)行lookUpImpOrForward,這樣就不會(huì)報(bào)錯(cuò)了。
重點(diǎn):lookUpImpOrForward這個(gè)方法會(huì)執(zhí)行兩次,第一次是動(dòng)態(tài)方法解析,第二次是慢速轉(zhuǎn)發(fā)過(guò)程。