上一節(jié)在描述Method數(shù)據(jù)結(jié)構(gòu)時(shí)晴及,區(qū)分了SEL和IMP。知道了在OC中是通過(guò)發(fā)送消息來(lái)執(zhí)行代碼的。
消息發(fā)送的流程也只有兩步:
- 通過(guò)SEL查找IMP。
- 執(zhí)行IMP阳液。
那么SEL是怎樣去查找IMP的呢?如果找不到會(huì)怎么辦揣炕?
1.1 首先會(huì)在本類找帘皿,如果找不到則會(huì)在其繼承鏈上尋找。
1.2 在動(dòng)態(tài)綁定的方法中尋找IMP畸陡。
1.3 消息轉(zhuǎn)發(fā)鹰溜。
由于前面兩步都沒(méi)有提供回調(diào),所以只需要將注意力放在第三步消息轉(zhuǎn)發(fā)上面丁恭。
消息轉(zhuǎn)發(fā)又分成3個(gè)步驟:
1.3.1 動(dòng)態(tài)方法解析
1.3.2 備援接收者
1.3.3 完整消息轉(zhuǎn)發(fā)
一個(gè)完整SEL查找IMP的過(guò)程如圖:
動(dòng)態(tài)方法解析
在沒(méi)有找到IMP時(shí)曹动,首先會(huì)調(diào)用下面的方法:
//類方法
+ (BOOL)resolveClassMethod:(SEL)sel
//實(shí)例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
在這個(gè)方法中,只需要給對(duì)象動(dòng)態(tài)添加一個(gè)方法并且返回YES牲览,表明可以處理墓陈。
//自定義的C函數(shù)。
void messageBirthDate(){
NSLog(@"CType :MessageForward birthDate");
}
//@param sel 方法的選擇子
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if ([@"messageBirthDate" isEqualToString:NSStringFromSelector(selector)]){
//添加方法
//@param class 對(duì)象的類
//@param sel 選擇子
//@param IMP IMP
//@param types 參數(shù)和返回類型編碼
class_addMethod([self class], selector, (IMP)messageBirthDate, "vv");
return YES;
}
return [super resolveInstanceMethod:selector];
}
備援接受者
如果動(dòng)態(tài)方法解析沒(méi)有進(jìn)行處理第献,則會(huì)調(diào)用下面的方法跛蛋,看能否進(jìn)行處理:
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(birthDate))
{
return [SonClass new];
}
return [super forwardingTargetForSelector:aSelector];
}
這個(gè)方法實(shí)際上是將消息轉(zhuǎn)發(fā)到有對(duì)應(yīng)SEL的類去進(jìn)行處理。
完整的消息轉(zhuǎn)發(fā)
如果備援接受者依然沒(méi)有進(jìn)行處理痊硕,則會(huì)進(jìn)行完整的消息轉(zhuǎn)發(fā)赊级。完整的消息轉(zhuǎn)發(fā)其實(shí)也就只有2步:
1.獲取方法簽名
2.執(zhí)行函數(shù)調(diào)用相關(guān)內(nèi)容。
//////////////消息轉(zhuǎn)發(fā)
//1.獲取方法簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
FatherClass *father = [[FatherClass alloc] init];
signature = [father methodSignatureForSelector:@selector(birthDate)];//將fatherBirthDate的Signature轉(zhuǎn)成 → birthDate的Signature
}
return signature;
}
//2.執(zhí)行函數(shù)調(diào)用相關(guān)
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
//自己新組成一個(gè)invokation岔绸。
// NSMethodSignature *signature= [anInvocation methodSignature];//可以從前面anInvocation中去取理逊。
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"@@:"];//也可以自己去生成橡伞。
NSInvocation *newInvocation =[NSInvocation invocationWithMethodSignature:signature];
FatherClass *father =[[FatherClass alloc] init];
[newInvocation setTarget:father];
[newInvocation setSelector:@selector(birthDate)];
[newInvocation invoke];
id retValue;
[newInvocation getReturnValue:&retValue];
NSLog(@"%@",(NSString *)retValue);
[anInvocation setReturnValue:&retValue];
}
這里需要注意變量的作用域,在setTarget時(shí)晋被,newInvocation不會(huì)去持有Target兑徘。所以需要自己控制變量的作用域。
在消息轉(zhuǎn)發(fā)過(guò)程中羡洛,越靠后消息處理的成本也越大挂脑,但是能夠進(jìn)行的掌控也越多。至于需要在哪個(gè)部分進(jìn)行截獲處理欲侮,就需要按照具體需求來(lái)判斷了崭闲。
異常處理
當(dāng)完整詳細(xì)轉(zhuǎn)發(fā)還不能夠處理時(shí),則會(huì)走到異常處理了:
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
[super doesNotRecognizeSelector:aSelector];
}
在異常處理中威蕉,可以做一些異常上報(bào)的工作刁俭。
總結(jié)
1.介紹了SEL尋找IMP的過(guò)程。
2.介紹了消息轉(zhuǎn)發(fā)的過(guò)程韧涨。
參考: