_objc_msgForward是IMP類型的全蝶,用于消息轉(zhuǎn)發(fā)的闹蒜,當(dāng)像一個(gè)對(duì)象發(fā)送消息,但他沒有實(shí)現(xiàn)的時(shí)候抑淫,_objc_msgForward會(huì)嘗試做消息轉(zhuǎn)發(fā)绷落。
objc_msgSend的動(dòng)作比較清晰,在“消息傳遞”過程中始苇,:首先在 Class 中的緩存查找 IMP (沒緩存則初始化緩存)砌烁,如果沒找到,則向父類的 Class 查找催式。如果一直查找到根類仍舊沒有實(shí)現(xiàn)函喉,則用_objc_msgForward函數(shù)指針代替 IMP 。最后荣月,執(zhí)行這個(gè) IMP 管呵。
_objc_msgForward消息轉(zhuǎn)發(fā)需要做的幾件事:
1. 調(diào)用+ (BOOL)resolveInstanceMethod:(SEL)sel(或 + (BOOL)resolveClassMethod:(SEL)sel)方法,在此方法中添加相應(yīng)selector以及IMP即可哺窄,允許用戶在此時(shí)為該Class動(dòng)態(tài)添加實(shí)現(xiàn)捐下。如果有實(shí)現(xiàn)了账锹,則調(diào)用并返回YES,那么重新開始o(jì)bjc_msgSend流程坷襟。對(duì)象會(huì)相應(yīng)這個(gè)選擇器奸柬,一般是因?yàn)樗呀?jīng)調(diào)用過class_addMethod。如果仍沒實(shí)現(xiàn)啤握,繼續(xù)下面的步驟
2. 調(diào)用- (id)forwardingTargetForSelector:(SEL)aSelector方法鸟缕,嘗試找到一個(gè)能相應(yīng)該消息的對(duì)象。如果獲取到排抬,則直接把消息轉(zhuǎn)發(fā)給它懂从,返回非nil對(duì)象。否則返回 nil 蹲蒲,繼續(xù)下面的動(dòng)作番甩。
3. 調(diào)用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法,嘗試獲得一個(gè)方法簽名届搁。如果能獲取缘薛,則返回非nil:創(chuàng)建一個(gè) NSlnvocation 并傳給forwardInvocation:
調(diào)用- (void)forwardInvocation:(NSInvocation *)anInvocation方法,將獲取到的方法簽名包裝成 Invocation 傳入卡睦,如何處理就在這里面了宴胧,并返回非nil。如果獲取不到表锻,則直接調(diào)用4拋出異常恕齐。
4. 調(diào)用- (void)doesNotRecognizeSelector:(SEL)aSelector,默認(rèn)的實(shí)現(xiàn)是拋出異常瞬逊。如果第3步?jīng)]能獲得一個(gè)方法簽名显歧,執(zhí)行該步驟。
上面前4個(gè)方法均是模板方法确镊,開發(fā)者可以override士骤,由 runtime 來調(diào)用。最常見的實(shí)現(xiàn)消息轉(zhuǎn)發(fā):就是重寫步驟3的兩個(gè)方法蕾域,吞掉一個(gè)消息或者代理給其他對(duì)象都是沒問題的
也就是說_objc_msgForward在進(jìn)行消息轉(zhuǎn)發(fā)的過程中會(huì)涉及以下這幾個(gè)方法:
- (BOOL)resolveInstanceMethod:(SEL)sel方法 (或 + (BOOL)resolveClassMethod:(SEL)sel)拷肌。
- (id)forwardingTargetForSelector:(SEL)aSelector方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法
- (void)forwardInvocation:(NSInvocation *)anInvocation方法
- (void)doesNotRecognizeSelector:(SEL)aSelector 方法
一共有三次機(jī)會(huì)。假設(shè)有A類和B類旨巷,分別對(duì)應(yīng)a對(duì)象廓块,b對(duì)象,a執(zhí)行了一個(gè)不存在的方法
[self performSelector:@selector(sel:) withObject:@"haha"];
第一次機(jī)會(huì)契沫,在+ (BOOL)resolveInstanceMethod:(SEL)sel方法中可以通過class_addMethod(self.class, sel, (IMP)dynamicMethodIMP, "@@:"); 添加動(dòng)態(tài)方法
id dynamicMethodIMP(id self, SEL _cmd, NSString *str)
{
NSLog(@"%s:動(dòng)態(tài)添加的方法",__FUNCTION__);
NSLog(@"%@", str);
return @"1";
}
添加成功后不會(huì)再繼續(xù)執(zhí)行
第二次機(jī)會(huì)带猴,- (id)forwardingTargetForSelector:(SEL)aSelector 系統(tǒng)給了個(gè)將這個(gè)SEL轉(zhuǎn)給其他對(duì)象的機(jī)會(huì),返回b對(duì)象懈万,b會(huì)重新走一次本過程拴清。
注意靶病,這里不要返回 self,會(huì)形成死循環(huán)口予。
第三次機(jī)會(huì)娄周,這個(gè)函數(shù)和后面的forwardInvocation:是最后一個(gè)尋找IML的機(jī)會(huì)。這個(gè)函數(shù)讓重載方有機(jī)會(huì)拋出一個(gè)函數(shù)的簽名沪停,再由后面的forwardInvocation:去執(zhí)行煤辨。
doesNotRecognizeSelector作為找不到函數(shù)實(shí)現(xiàn)的最后一步,NSObject實(shí)現(xiàn)這個(gè)函數(shù)只有一個(gè)功能木张,就是拋出異常众辨。
雖然理論上可以重載這個(gè)函數(shù)實(shí)現(xiàn)保證不拋出異常(不調(diào)用super實(shí)現(xiàn)),但是蘋果文檔著重提出“一定不能讓這個(gè)函數(shù)就這么結(jié)束掉舷礼,必須拋出異尘槌梗”。
總結(jié)一下妻献,在一個(gè)函數(shù)找不到時(shí)蛛株,Objective-C提供了三種方式去補(bǔ)救:
1、調(diào)用resolveInstanceMethod給個(gè)機(jī)會(huì)讓類添加這個(gè)實(shí)現(xiàn)這個(gè)函數(shù)
2育拨、調(diào)用forwardingTargetForSelector讓別的對(duì)象去執(zhí)行這個(gè)函數(shù)
3谨履、調(diào)用methodSignatureForSelector(函數(shù)符號(hào)制造器)和forwardInvocation(函數(shù)執(zhí)行器)靈活的將目標(biāo)函數(shù)以其他形式執(zhí)行。
如果都不行熬丧,調(diào)用doesNotRecognizeSelector拋出異常笋粟。
直接調(diào)用_objc_msgForward很危險(xiǎn),如果用不好會(huì)直接導(dǎo)致程序Crash锹引。
_objc_msgForward隸屬 C 語言,有三個(gè)參數(shù)
_objc_msgForward是 IMP 類型唆香,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個(gè)對(duì)象發(fā)送一條消息嫌变,但它并沒有實(shí)現(xiàn)的時(shí)候,_objc_msgForward會(huì)嘗試做消息轉(zhuǎn)發(fā)躬它。一旦調(diào)用_objc_msgForward腾啥,將跳過查找 IMP 的過程,直接觸發(fā)“消息轉(zhuǎn)發(fā)”冯吓。