消息轉發(fā)機制
假設說我們聲明一個類, 初始化對象, 并且在此類聲明一個方法, 調用方法的時候底層是怎么處理的呢? 今天我們來簡單模擬測試, 來看道理發(fā)生了什么
以下是調用方法處理的方案圖, 按照方案順序去處理
以下是系統(tǒng)方法
//消息轉發(fā)
//-?(id)forwardingTargetForSelector:(SEL)aSelector?OBJC_AVAILABLE(10.5,?2.0,?9.0,?1.0,?2.0);
//標準的消息轉發(fā)
//-?(void)forwardInvocation:(NSInvocation?*)anInvocation?OBJC_SWIFT_UNAVAILABLE("");
//-?(NSMethodSignature?*)methodSignatureForSelector:(SEL)aSelector?OBJC_SWIFT_UNAVAILABLE("");
//
//動態(tài)方法解析
//+?(BOOL)resolveClassMethod:(SEL)sel?OBJC_AVAILABLE(10.5,?2.0,?9.0,?1.0,?2.0);
//+?(BOOL)resolveInstanceMethod:(SEL)sel?OBJC_AVAILABLE(10.5,?2.0,?9.0,?1.0,?2.0);
Demo解析
基礎步驟
創(chuàng)建類, Person類, 聲明方法, 并且在ViewController進行初始化調用
VC中
Person?*person?=?[Personnew];
[person?run];
Person類中
-?(void)run;//沒有實現(xiàn)
此時運行是不是會報錯呢? 就是這個常見的錯誤
“ - [Person run]: unrecognized selector sent to instance 0x600000008310’ “
那么這樣做到底發(fā)生了什么? 做了哪些事情? 我們一步步來剖析
動態(tài)測試
在Presenter類中, 寫動態(tài)方法
+?(BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"sel?=?%@",NSStringFromSelector(sel));
return[superresolveInstanceMethod:sel];
}
再次運行Demo就會走到這個方法中, 也就是我們所指的方案1, 此時打印出來的scl為” 消息轉發(fā)機制Demo[41829:4186268] sel = run “
解析模擬
+?(BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"sel?=?%@",NSStringFromSelector(sel));
//1.判斷沒有實現(xiàn)方法,?那么我們就是動態(tài)添加一個方法
if(sel?==?@selector(run:))?{
class_addMethod(self,?sel,?(IMP)newRun,"v@:@:");
returnYES;
}
return[superresolveInstanceMethod:sel];
}
聲明函數(shù)
voidnewRun(id?self,SEL?sel,NSString?*str)?{
NSLog(@"---runok---%@",str);
}
溫馨小提示, 動態(tài)添加方法參數(shù)意譯 : //將要添加方法的類/sel名/IMP函數(shù)指針<添加函數(shù)>, 官方文檔其實是有解釋的
此時我們再次運行, 那么打印結果就來了” 消息轉發(fā)機制Demo[43269:4212899] —runok—ok跑 “, 這樣的話我們就解決掉了報錯這個問題
消息轉發(fā)重定向測試
此時我們新創(chuàng)建一個類Mbxb, 此時我們還是重新寫一個同名字的方法run方法, 并且進行實現(xiàn)
-?(void)run{
NSLog(@"---Mbxbrunok---");
}
解析
此時有兩個同樣的方法, 我們重新在Person類中
來實現(xiàn)方法
-?(id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"aSelector?=?%@",NSStringFromSelector(aSelector));
return[superforwardingTargetForSelector:aSelector];
}
此時運行測試, 動態(tài)測試輸出” 消息轉發(fā)機制Demo[45875:4255869] sel = run “, 消息轉發(fā)重定向輸出” 消息轉發(fā)機制Demo[45875:4255869] —Mbxbrunok— “, 同樣也可以找見方法run
當我們進行處理
-?(id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"aSelector?=?%@",NSStringFromSelector(aSelector));
return[[Mbxb?alloc]init];
}
那么此時運行成功輸出, “ —Mbxbrunok— “
生成方法簽名轉發(fā)消息
此時我們在Person類中, 生成方法簽名
-?(NSMethodSignature?*)methodSignatureForSelector:(SEL)aSelector?OBJC_SWIFT_UNAVAILABLE(""){
//轉化字符
NSString?*sel?=?NSStringFromSelector(aSelector);
//判斷,?手動生成簽名
if([sel?isEqualToString:@"run"]){
return[NSMethodSignature?signatureWithObjCTypes:"v@:"];
}else{
return[supermethodSignatureForSelector:aSelector];
}
拿到簽名
-?(void)forwardInvocation:(NSInvocation?*)anInvocation?OBJC_SWIFT_UNAVAILABLE(""){
NSLog(@"---%@---",anInvocation);
return[superforwardInvocation:anInvocation];
}
此時我們的po的簽名輸出為” return value: {v} void target: {@} 0x600000016ba0 selector: {:} run “
拿到消息轉發(fā)簽名
-?(void)forwardInvocation:(NSInvocation?*)anInvocation?OBJC_SWIFT_UNAVAILABLE(""){
NSLog(@"---%@---",anInvocation);
//取到消息
SEL?seletor?=?[anInvocation?selector];
//轉發(fā)
Mbxb?*bxb?=?[[Mbxb?alloc]init];
if([bxb?respondsToSelector:seletor]){
//調用對象,進行轉發(fā)
[anInvocation?invokeWithTarget:bxb];
}else{
return[superforwardInvocation:anInvocation];
}
}
小細節(jié): 拋出異常
假如說我們沒有這個方法, 同樣是遇到會崩潰的問題
我們這里來進行一個異常處理
-?(void)doesNotRecognizeSelector:(SEL)aSelector{
NSString?*selStr?=?NSStringFromSelector(aSelector);
NSLog(@"%@不存在",selStr);
}
我們可以在這個異常處理中一些處理, 比如說彈框
總結
對于消息轉發(fā)機制, 我們重新來梳理一下Demo解析思路
還是三個方案, 按順序來走
動態(tài)方法解析
消息轉發(fā)重定向
生成方法簽名
拿到簽名轉發(fā)消息
細節(jié)處理, 拋出異常
最后獻上一張邏輯圖