動(dòng)態(tài)方法解析
方法沒有查詢到的時(shí)候仅炊,運(yùn)行時(shí)會(huì)調(diào)用 - (BOOL)resolveInstanceMethod:
或者 + (BOOL)resolveClassMethod:
弄诲,定義在NSObject中,默認(rèn)實(shí)現(xiàn)如下:
+ (BOOL)resolveClassMethod:(SEL)sel {
return NO;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
return NO;
}
即默認(rèn)不會(huì)進(jìn)行動(dòng)態(tài)方法解析盯串,如果在自定義的類中重寫了該函數(shù)并且添加了動(dòng)態(tài)解析方法氯檐, 那消息查找的最后階段就會(huì)調(diào)用到動(dòng)態(tài)解析方法。以實(shí)例方法為例如下:
void dynamicIMP(id self, SEL _cmd) {
NSLog(@"do something");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(MissMethod)) {
class_addMethod([self class], sel, (IMP)dynamicIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
"v@:"中v
代表函數(shù)返回值類型為void
体捏,@
代表self的類型為id
冠摄,:
代表_cmd的類型為SEL
。當(dāng)調(diào)用到MissMethod的時(shí)候几缭,就會(huì)執(zhí)行dynamicIMP方法
消息轉(zhuǎn)發(fā)
動(dòng)態(tài)方法解析失敗后流程會(huì)被標(biāo)記河泳,隨即再次觸發(fā)消息查詢,此時(shí)會(huì)跳過動(dòng)態(tài)方法解析流程直接進(jìn)行消息轉(zhuǎn)發(fā)年栓。同樣定義在NSObject中拆挥,以對(duì)象方法為例- (id)forwardingTargetForSelector:(SEL)aSelector
默認(rèn)實(shí)現(xiàn)如下:
- (id)forwardingTargetForSelector:(SEL)aSelector {
return nil;
}
默認(rèn)返回nil不修改消息接受者,修改如下某抓,這樣當(dāng)對(duì)象接收到MissMethod消息的時(shí)候纸兔,就會(huì)執(zhí)行CommonClass的MissMethod方法惰瓜,當(dāng)然會(huì)走一次CommonClass的消息查找流程
- (id)forwardingTargetForSelector:(SEL)aSelector {
if(aSelector == @selector(MissMethod)) {
return [[CommonClass alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
獲取方法簽名執(zhí)行完整消息轉(zhuǎn)發(fā)
當(dāng)消息轉(zhuǎn)發(fā)失敗的時(shí)候,會(huì)執(zhí)行methodSignatureForSelector獲取方法簽名汉矿,獲取成功后會(huì)執(zhí)行forwardInvocation進(jìn)行消息分發(fā)崎坊,以對(duì)象方法為例默認(rèn)實(shí)現(xiàn)如下:
// Replaced by CF (returns an NSMethodSignature)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
_objc_fatal("-[NSObject methodSignatureForSelector:] "
"not available without CoreFoundation");
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
}
默認(rèn)會(huì)執(zhí)行doesNotRecognizeSelector直接拋出異常,修改如下
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
if (!methodSignature) {
methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:*"];
}
return methodSignature;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
SEL sel = invocation.selector;
CommonClass *obj = [[CommonClass alloc] init];
if([obj respondsToSelector:sel]) {
[invocation invokeWithTarget:obj];
} else {
[self doesNotRecognizeSelector:sel];
}
}
如上獲取成功的方法簽名用于生成NSInvocation對(duì)象洲拇,invocation對(duì)象通過invokeWithTarget:方法將消息發(fā)送給obj對(duì)象奈揍,同樣會(huì)走一遍CommonClass的消息查找流程,值得注意的是這里可以同時(shí)發(fā)送消息給多個(gè)對(duì)象赋续,對(duì)于一些一對(duì)多的傳值模型可以考慮用這種方案
總結(jié)
可以看到蘋果的消息轉(zhuǎn)發(fā)機(jī)制是很有邏輯的
1男翰、消息查找階段沒有匹配到IMP,會(huì)進(jìn)入方法動(dòng)態(tài)解析流程蚕捉,執(zhí)行
resolveInstanceMethod
奏篙,你可能想在本類搞些事情
2柴淘、動(dòng)態(tài)解釋流程失敗后進(jìn)入消息轉(zhuǎn)發(fā)流程迫淹,執(zhí)行forwardingTargetForSelector
,你可能想在其他類搞些事情
3为严、消息轉(zhuǎn)發(fā)流程失敗后會(huì)啟動(dòng)完整消息轉(zhuǎn)發(fā)機(jī)制敛熬,獲取方法簽名methodSignatureForSelector
,執(zhí)行forwardInvocation
方法進(jìn)行消息分發(fā)第股,你可能在其他很多類搞很多事情