消息轉(zhuǎn)發(fā)機(jī)制

前言

在上一篇Runtime源碼 方法調(diào)用的過程中我們了解了消息的響應(yīng)過程,即

  1. 先緩存查找,若未找到
  2. 接下來查找本類的方法列表查找,若未找到
  3. 則遞歸繼承體系查找父類的方法列表直到NSObject

在第二第三步過程中,如果找到了則響應(yīng)消息,并填充緩存,緩存是保存在元類上的,如果還找不到,則進(jìn)入接下來的消息轉(zhuǎn)發(fā)流程。

消息轉(zhuǎn)發(fā)流程也分為三個(gè)步驟,動(dòng)態(tài)解析 -> 前端轉(zhuǎn)發(fā) -> 方法簽名轉(zhuǎn)發(fā),流程如下:

消息轉(zhuǎn)發(fā)

我們結(jié)合一個(gè)實(shí)例來具體看看傳送門:QFMessageForwardDemo

動(dòng)態(tài)解析

第一步,當(dāng)沒找到方法時(shí),你可以通過+ (BOOL)resolveInstanceMethod:(SEL)sel+ (BOOL)resolveClassMethod:(SEL)sel來添加實(shí)例方法和類方法

這里我們?cè)?code>ViewController中直接調(diào)用了QFPersonrun方法,但是QFPerson并沒有實(shí)現(xiàn)這個(gè)方法,所以動(dòng)態(tài)添加了這個(gè)方法。

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    
    if (sel == @selector(run:age:)) {//第一步自己添加方法
        class_addMethod(self, sel, (IMP)newRun, "v@:@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

這個(gè)添加一個(gè)自定義的方法

void newRun(id self,SEL sel, NSString *str, NSInteger age) {//自定義方法實(shí)現(xiàn)
    NSLog(@"---run ok---%@---%ld",str,(long)age);
}

這里用到了runtime的動(dòng)態(tài)添加方法,不熟悉的可以看看這個(gè)系列文章的前幾篇
,執(zhí)行結(jié)果:

QFMessageForwardDemo[18572:704661] ---run ok---hello---18

前端轉(zhuǎn)發(fā)

如果第一步?jīng)]有動(dòng)態(tài)添加方法,則會(huì)進(jìn)入轉(zhuǎn)發(fā)的第二步,前端轉(zhuǎn)發(fā),所謂前端轉(zhuǎn)發(fā)即是,本類沒有實(shí)現(xiàn)這個(gè)方法,但是另外的一個(gè)類實(shí)現(xiàn)了這個(gè)方法,那么我們可以直接轉(zhuǎn)發(fā)這條消息到另外的類實(shí)現(xiàn)調(diào)用。這里我們新建QFOtherPerson```` 并且實(shí)現(xiàn)- (void)run:(NSString *)name age:(NSInteger)age```方法

- (void)run:(NSString *)name age:(NSInteger)age{
    NSLog(@"%@ 執(zhí)行了 run name = %@,age = %ld",NSStringFromClass([self class]), name,age);
}

QFPerson的實(shí)現(xiàn)文件中,實(shí)現(xiàn)下面的方法完成轉(zhuǎn)發(fā)

- (id)forwardingTargetForSelector:(SEL)aSelector {
    return [[QFOtherPerson alloc]init];//第二步,前端轉(zhuǎn)發(fā)
}

ps:此時(shí)要先注釋注釋掉第一步的動(dòng)態(tài)添加方法

執(zhí)行結(jié)果:

QFMessageForwardDemo[19629:739692] QFOtherPerson 執(zhí)行了 run name = hello,age = 18

簽名轉(zhuǎn)發(fā)

第三步,也是最后的機(jī)會(huì)處理這條消息了,首先生成方法簽名:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {//第三步,簽名轉(zhuǎn)發(fā)
    NSString *methodName = NSStringFromSelector(aSelector);
    if ([methodName isEqualToString:@"run:age:"]) {//是我們需要轉(zhuǎn)發(fā)的run方法
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    } else {
        return [super methodSignatureForSelector:aSelector];
    }
}

關(guān)于invocation你熟悉的可以看ResponderChain+Strategy+MVVM實(shí)現(xiàn)一個(gè)優(yōu)雅的TableView這里面有完整的invocation調(diào)用方法的例子,這里就不做粘貼了。

最后調(diào)用-forwardInvocation:指定target,完成轉(zhuǎn)發(fā):

- (void)forwardInvocation:(NSInvocation *)anInvocation {//簽名轉(zhuǎn)發(fā)
    SEL selector = [anInvocation selector];//目標(biāo)方法

    QFOtherPerson *other = [[QFOtherPerson alloc]init];//轉(zhuǎn)發(fā)對(duì)象

    if ([other respondsToSelector:selector]) {//目標(biāo)對(duì)象能相應(yīng)此方法
        [anInvocation invokeWithTarget:other];
    } else {
        return [super forwardInvocation:anInvocation];
    }
}

運(yùn)行結(jié)果:

QFMessageForwardDemo[31317:930989] QFOtherPerson 執(zhí)行了 run name = hello,age = 18

以上就是方法轉(zhuǎn)發(fā)的流程,如果以上三步都沒有實(shí)現(xiàn)則會(huì)崩潰,顯示一個(gè)很常見的異常- unrecognized selector sent to instance 0x60000001b440'

關(guān)于之前的流程請(qǐng)參考:Runtime源碼 方法調(diào)用的過程

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容