前言
在之前的的objc_msgSend()
探索中,當(dāng)調(diào)用一個(gè)方法后乍桂,首先會(huì)進(jìn)入快速查找來匹配sel
對(duì)應(yīng)的imp
冲杀;當(dāng)沒有找到時(shí),會(huì)進(jìn)入慢速查找模蜡,開始匹配漠趁;當(dāng)依然無法匹配時(shí),蘋果給出了一次轉(zhuǎn)發(fā)的機(jī)會(huì)(動(dòng)態(tài)方法決議):
// No implementation found. Try method resolver once.
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
從注釋:// No implementation found. Try method resolver once.
也是一目了然忍疾。
這篇文章闯传,我們就一起探索下resolveMethod_locked()
的內(nèi)部實(shí)現(xiàn)。
開始
直接看下resolveMethod_locked()
的源碼:
static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
// 方法沒有你怎么不知道
// 報(bào)錯(cuò)
// 給你一次機(jī)會(huì)
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);
}
首先判斷當(dāng)前的cls
是否為元類
,當(dāng)不是元類
則調(diào)用resolveInstanceMethod()
,否則調(diào)用resolveClassMethod()
卤妒。接下來分別看下兩個(gè)方法的具體實(shí)現(xiàn):
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
/***********************************************************************
* resolveInstanceMethod
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
* cls may be a metaclass or a non-meta class.
* Does not check if the method already exists.
**********************************************************************/
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));
}
}
}
該方法一開始的注釋寫的就很清楚:
- Call +resolveInstanceMethod, looking for a method to be added to class cls.
意味著甥绿,resolveInstanceMethod
執(zhí)行會(huì)響應(yīng)+resolveInstanceMethod
,我們可以通過在類中實(shí)現(xiàn)該方法字币,來接收響應(yīng),并做處理共缕。同時(shí)我們可以看到發(fā)起響應(yīng)的關(guān)鍵代碼:
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);
即通過objc_msgSend
向cls
發(fā)送了一個(gè)resolveInstanceMethod
消息洗出。
static void resolveClassMethod(id inst, SEL sel, Class cls)
//在cls中添加并實(shí)現(xiàn)
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
//自定義動(dòng)態(tài)處理
return [super resolveInstanceMethod:sel];
}
再來看下 resolveClassMethod
的具體實(shí)現(xiàn)
/***********************************************************************
* resolveClassMethod
* Call +resolveClassMethod, looking for a method to be added to class cls.
* cls should be a metaclass.
* Does not check if the method already exists.
**********************************************************************/
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));
}
}
}
同樣在方法的注釋里面也寫的很清楚,在cls
中添加并實(shí)現(xiàn)該方法來接收響應(yīng)图谷,關(guān)鍵的觸發(fā)響應(yīng)代碼翩活,同樣是通過objc_msgSend
發(fā)送消息,跟resolveInstanceMethod的邏輯如出一轍:
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
回到我們的類:
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
//添加自定義處理
return [super resolveInstanceMethod:sel];
}
+ (BOOL)resolveClassMethod:(SEL)sel
{
//添加自定義處理
return [super resolveClassMethod:sel];
}
消息轉(zhuǎn)發(fā)
當(dāng)方法決議后依然沒有匹配到imp
時(shí)便贵,會(huì)觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制菠镇,由libObjC
轉(zhuǎn)向CoreFoundation
處理并觸發(fā)轉(zhuǎn)發(fā)回調(diào),這個(gè)可以在Crash時(shí)的堆棧信息中看到:
關(guān)于
CoreFoundation
的源碼這里就不展開驗(yàn)證了(需要借助一些反編譯工具查看一些偽代碼)承璃。此時(shí)會(huì)觸發(fā)以下幾個(gè)熟悉的回調(diào)函數(shù):
//快速轉(zhuǎn)發(fā)流程
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return nil;
}
//慢速轉(zhuǎn)發(fā)流程
//1.方法簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
//2.轉(zhuǎn)發(fā)處理
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// [anInvocation invoke];
}
//當(dāng)沒有實(shí)現(xiàn)任何處理時(shí)
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
}
在日常的開發(fā)中利耍,我們可以在適當(dāng)?shù)奈恢茫鲰憫?yīng)的容錯(cuò)處理代碼盔粹。
消息轉(zhuǎn)發(fā)簡(jiǎn)單流程圖
這里解釋下
resolveClassMethod
->resolveInstanceMethod
:當(dāng)調(diào)用類方法的時(shí)候隘梨,
Objc
會(huì)去該類
的元類
中去查找對(duì)應(yīng)的IMP
,沒有找到會(huì)去他的父元類
中查找,最終會(huì)查詢到根元類
舷嗡,根元類
依然沒有找到轴猎,會(huì)繼續(xù)去根元類的父類
,即根類(NSObject)
中進(jìn)行查找咬崔,而類中只有實(shí)例方法税稼,此時(shí)objc內(nèi)部做了一些處理,當(dāng)在根元類
無法找到IMP
時(shí)垮斯,直接響應(yīng)父類(NSObject類)
的resolveInstanceMethod
方法郎仆,可以在如下的源碼中體現(xiàn):
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);
}
總結(jié)
至此,我們能看到兜蠕,在我們程序?qū)⒁l(fā)生崩潰時(shí)扰肌,我們有兩次避免崩潰的機(jī)會(huì),即動(dòng)態(tài)方法決議和消息轉(zhuǎn)發(fā)處理熊杨,前者是在libObjC
下觸發(fā)曙旭,或者在CoreFoundation
下觸發(fā)。在實(shí)際開發(fā)中我們應(yīng)該盡量避免在方法決議層做邏輯處理晶府,而是在更底層的消息轉(zhuǎn)發(fā)層做相應(yīng)的容錯(cuò)處理桂躏。