發(fā)送消息后:(eg.[instanceperformSelector:@selector(selname)])
1.
先一層一層尋找方法
Runtime在GitHub開源了一部分源碼:?objc4
objc-runtime-new.mm文件的 lookUpImpOrForward 方法中可以看到從子到父尋找的過程菠剩。?
如果找不到磷雇,進(jìn)入第二步
2.
可以看到源碼鲜漩,這里調(diào)用了_class_resolveMethod方法
if(resolver? &&? !triedResolver) {
runtimeLock.unlockRead();
_class_resolveMethod(cls, sel, inst);
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver =YES;
gotoretry;
}
就是NSObject的這個(gè)方法:(BOOL)resolveInstanceMethod:(SEL)sel
在這個(gè)方法中可以新加方法惠奸,
返回值:YESif the method was found and added to the receiver, otherwiseNO.
但是源碼里并沒有對(duì)返回值進(jìn)行判斷,就是說無論返回yes或者no私痹,都會(huì)retry,并且只要addmethod成功就算返回NO也依然會(huì)調(diào)用匠抗。
還沒找到就進(jìn)入第三步:
3.
// If neither self nor its superclasses implement
// it, forward the message because self might know
// someone who does.? This is a "chained" forward...
if (! size) return [self forward: sel: args];
源碼如上故源,這里應(yīng)該是開始調(diào)用下面這個(gè)方法:
?(id)forwardingTargetForSelector:(SEL)aSelector
如果一個(gè)對(duì)象實(shí)現(xiàn)了這個(gè)方法,并返回一個(gè)非nil的結(jié)果汞贸,則這個(gè)被返回的對(duì)象會(huì)作為消息的新接收者绳军,且消息會(huì)被分發(fā)到這個(gè)對(duì)象。當(dāng)然這個(gè)對(duì)象不能是self自身矢腻,否則就是出現(xiàn)無限循環(huán)门驾。當(dāng)然,如果我們沒有指定相應(yīng)的對(duì)象來處理aSelector多柑,則應(yīng)該調(diào)用父類的實(shí)現(xiàn)來返回結(jié)果奶是。
如果forward返回空,進(jìn)入第四步:
4.
// Message self with the specified selector and arguments
return objc_msgSendv (self, sel, size, args);
應(yīng)該是在objc_msgSendv方法中開始了下面的過程:
先調(diào)用
-?(NSMethodSignature?*)methodSignatureForSelector:(SEL)aSelector
獲取了NSMethodSignature竣灌, 再調(diào)用下面的forwardInvocation聂沙,如果沒有實(shí)現(xiàn)上述方法,NSObject的默認(rèn)實(shí)現(xiàn)是unrecognized selector初嘹。
-?(void)forwardInvocation:(NSInvocation?*)anInvocation
在這個(gè)方法中使用anInvocation-invoke 結(jié)束消息及汉;
如果沒有ivnoke 依然unrecognized selector
下面我們?cè)敿?xì)討論一下這四個(gè)步驟。
動(dòng)態(tài)方法解析
對(duì)象在接收到未知的消息時(shí)屯烦,首先會(huì)調(diào)用所屬類的類方法+resolveInstanceMethod:(實(shí)例方法)或 者+resolveClassMethod:(類方法)坷随。在這個(gè)方法中房铭,我們有機(jī)會(huì)為該未知消息新增一個(gè)”處理方法”“。不過使用該方法的前提是我們已經(jīng) 實(shí)現(xiàn)了該”處理方法”温眉,只需要在運(yùn)行時(shí)通過class_addMethod函數(shù)動(dòng)態(tài)添加到類里面就可以了缸匪。如下代碼所示:
void?functionForMethod1(id?self,?SEL?_cmd)?{
NSLog(@"%@,?%p",?self,?_cmd);
}
+?(BOOL)resolveInstanceMethod:(SEL)sel?{
NSString?*selectorString?=?NSStringFromSelector(sel);
if([selectorString?isEqualToString:@"method1"])?{
class_addMethod(self.class,?@selector(method1),?(IMP)functionForMethod1,"@:");
}
return [super resolveInstanceMethod:sel];
}
備用接收者
如果在上一步無法處理消息,則Runtime會(huì)繼續(xù)調(diào)以下方法:
-?(id)forwardingTargetForSelector:(SEL)aSelector
如果一個(gè)對(duì)象實(shí)現(xiàn)了這個(gè)方法芍殖,并返回一個(gè)非nil的結(jié)果豪嗽,則這個(gè)對(duì)象會(huì)作為消息的新接收者,且消息會(huì)被分發(fā)到這個(gè)對(duì)象豌骏。當(dāng)然這個(gè)對(duì)象不能是self自身龟梦,否則就是出現(xiàn)無限循環(huán)。當(dāng)然窃躲,如果我們沒有指定相應(yīng)的對(duì)象來處理aSelector计贰,則應(yīng)該調(diào)用父類的實(shí)現(xiàn)來返回結(jié)果。
使用這個(gè)方法通常是在對(duì)象內(nèi)部蒂窒,可能還有一系列其它對(duì)象能處理該消息躁倒,我們便可借這些對(duì)象來處理消息并返回,這樣在對(duì)象外部看來洒琢,還是由該對(duì)象親自處理了這一消息秧秉。如下代碼所示:
@interface?SUTRuntimeMethodHelper?:?NSObject
-?(void)method2;
@end@implementation?SUTRuntimeMethodHelper
-?(void)method2?{
NSLog(@"%@,?%p",?self,?_cmd);
}
@end
#pragma?mark?-
@interface?SUTRuntimeMethod?()?{SUTRuntimeMethodHelper?*_helper;}
@end
@implementation?SUTRuntimeMethod
+?(instancetype)object?{
return [[self?alloc]?init];
}
-?(instancetype)init?{
self?=?[super init];
if(self?!=?nil)?{
_helper?=?[[SUTRuntimeMethodHelper?alloc]?init];
}
return self;
}
-?(void)test?{
[self?performSelector:@selector(method2)];
}
-?(id)forwardingTargetForSelector:(SEL)aSelector?{
NSLog(@"forwardingTargetForSelector");
NSString?*selectorString?=?NSStringFromSelector(aSelector);
//?將消息轉(zhuǎn)發(fā)給_helper來處理
if([selectorString?isEqualToString:@"method2"])?{
return_helper;
}
return[superforwardingTargetForSelector:aSelector];
}
@end
這一步合適于我們只想將消息轉(zhuǎn)發(fā)到另一個(gè)能處理該消息的對(duì)象上。但這一步無法對(duì)消息進(jìn)行處理衰抑,如操作消息的參數(shù)和返回值象迎。
完整消息轉(zhuǎn)發(fā)
如果在上一步還不能處理未知消息,則唯一能做的就是啟用完整的消息轉(zhuǎn)發(fā)機(jī)制了呛踊。此時(shí)會(huì)調(diào)用以下方法:
-?(void)forwardInvocation:(NSInvocation?*)anInvocation
運(yùn)行時(shí)系統(tǒng)會(huì)在這一步給消息接收者最后一次機(jī)會(huì)將消息轉(zhuǎn)發(fā)給其它對(duì)象砾淌。對(duì)象會(huì)創(chuàng)建一個(gè)表示消息的NSInvocation對(duì)象,把與尚未處理的消息 有關(guān)的全部細(xì)節(jié)都封裝在anInvocation中谭网,包括selector汪厨,目標(biāo)(target)和參數(shù)。我們可以在forwardInvocation 方法中選擇將消息轉(zhuǎn)發(fā)給其它對(duì)象愉择。
forwardInvocation:方法的實(shí)現(xiàn)有兩個(gè)任務(wù):
1. 定位可以響應(yīng)封裝在anInvocation中的消息的對(duì)象劫乱。這個(gè)對(duì)象不需要能處理所有未知消息。
2. 使用anInvocation作為參數(shù)薄辅,將消息發(fā)送到選中的對(duì)象要拂。anInvocation將會(huì)保留調(diào)用結(jié)果,運(yùn)行時(shí)系統(tǒng)會(huì)提取這一結(jié)果并將其發(fā)送到消息的原始發(fā)送者站楚。
不過脱惰,在這個(gè)方法中我們可以實(shí)現(xiàn)一些更復(fù)雜的功能,我們可以對(duì)消息的內(nèi)容進(jìn)行修改窿春,比如修改一個(gè)參數(shù)等拉一,然后再去觸發(fā)消息采盒。另外,若發(fā)現(xiàn)某個(gè)消息不應(yīng)由本類處理,則應(yīng)調(diào)用父類的同名方法,以便繼承體系中的每個(gè)類都有機(jī)會(huì)處理此調(diào)用請(qǐng)求康二。
還有一個(gè)很重要的問題,我們必須重寫以下方法:
-?(NSMethodSignature?*)methodSignatureForSelector:(SEL)aSelector
消息轉(zhuǎn)發(fā)機(jī)制使用從這個(gè)方法中獲取的信息來創(chuàng)建NSInvocation對(duì)象烦租。因此我們必須重寫這個(gè)方法,為給定的selector提供一個(gè)合適的方法簽名除盏。
完整的示例如下所示:
-?(NSMethodSignature?*)methodSignatureForSelector:(SEL)aSelector?{
? ? NSMethodSignature?*signature?=?[supermethodSignatureForSelector:aSelector];?
? ? if(!signature)?{
? ? ? ? if([SUTRuntimeMethodHelper?instancesRespondToSelector:aSelector])?{
? ? ? ? ? ? signature?=?[SUTRuntimeMethodHelper?instanceMethodSignatureForSelector:aSelector];
? ? ? ? }
? }
? ?returnsignature;
}
-?(void)forwardInvocation:(NSInvocation?*)anInvocation?{
? ? if([SUTRuntimeMethodHelper?instancesRespondToSelector:anInvocation.selector])?{
? ? ? ? [anInvocation?invokeWithTarget:_helper];
? ? }
}
NSObject的forwardInvocation:方法實(shí)現(xiàn)只是簡(jiǎn)單調(diào)用了doesNotRecognizeSelector:方法叉橱,它不會(huì)轉(zhuǎn)發(fā)任何消息。這樣者蠕,如果不在以上所述的三個(gè)步驟中處理未知消息窃祝,則會(huì)引發(fā)一個(gè)異常。
從某種意義上來講踱侣,forwardInvocation:就像一個(gè)未知消息的分發(fā)中心粪小,將這些未知的消息轉(zhuǎn)發(fā)給其它對(duì)象÷站洌或者也可以像一個(gè)運(yùn)輸站一樣將所有未知消息都發(fā)送給同一個(gè)接收對(duì)象探膊。這取決于具體的實(shí)現(xiàn)。
消息轉(zhuǎn)發(fā)與多重繼承
回過頭來看第二和第三步待榔,通過這兩個(gè)方法我們可以允許一個(gè)對(duì)象與其它對(duì)象建立關(guān)系突想,以處理某些未知消息,而表面上看仍然是該對(duì)象在處理消息究抓。通過這 種關(guān)系,我們可以模擬“多重繼承”的某些特性袭灯,讓對(duì)象可以“繼承”其它對(duì)象的特性來處理一些事情刺下。不過,這兩者間有一個(gè)重要的區(qū)別:多重繼承將不同的功能 集成到一個(gè)對(duì)象中稽荧,它會(huì)讓對(duì)象變得過大橘茉,涉及的東西過多;而消息轉(zhuǎn)發(fā)將功能分解到獨(dú)立的小的對(duì)象中姨丈,并通過某種方式將這些對(duì)象連接起來畅卓,并做相應(yīng)的消息轉(zhuǎn) 發(fā)。
不過消息轉(zhuǎn)發(fā)雖然類似于繼承蟋恬,但NSObject的一些方法還是能區(qū)分兩者翁潘。如respondsToSelector:和isKindOfClass:只能用于繼承體系,而不能用于轉(zhuǎn)發(fā)鏈歼争。便如果我們想讓這種消息轉(zhuǎn)發(fā)看起來像是繼承拜马,則可以重寫這些方法渗勘,如以下代碼所示:
-?(BOOL)respondsToSelector:(SEL)aSelector???{
if(?[superrespondsToSelector:aSelector]?)
returnYES;
else{
/*?
Here,?test?whether?the?aSelector?message?can be?forwarded?to?another?object?and?whether?that object?can?respond?to?it.?Return?YES?if?it?can.
*/
}
returnNO;
}
相關(guān)文章: