runtime 消息轉(zhuǎn)發(fā)機(jī)制(最詳細(xì)流程)

說(shuō)到runtime,就不得不講到最重要的消息轉(zhuǎn)發(fā)機(jī)制,轉(zhuǎn)發(fā)的流程圖如下:


173709-7bcc4302c515f1e0.png

在OC里面,所有的方法調(diào)用,在運(yùn)行時(shí)的時(shí)候都可以看作是objc_msgSend(obj,@selector())的調(diào)用

1.正常的方法調(diào)用

person.h文件,聲明方法

#import <Foundation/Foundation.h>

@interface Person : NSObject
-(void)sendMessage:(NSString *)msg;
@end

person.m文件

#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person
- (void)sendMessage:(NSString *)msg{
    NSLog(@"person send msg:%@",msg);
}
@end

如果person.m實(shí)現(xiàn)了-(void)sendMessage:(NSString *)msg 方法,那么將會(huì)調(diào)用該方法,結(jié)果如圖:


image.png

如果沒(méi)有實(shí)現(xiàn)這個(gè)方法,則會(huì)報(bào)錯(cuò)沒(méi)有該方法


image.png

那么實(shí)現(xiàn)消息轉(zhuǎn)發(fā)機(jī)制的方法,就可以解決這個(gè)問(wèn)題:

  1. 如果沒(méi)有實(shí)現(xiàn)改方法,會(huì)先進(jìn)行動(dòng)態(tài)解析,調(diào)用+ (BOOL)resolveInstanceMethod:(SEL)sel方法
#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person

//- (void)sendMessage:(NSString *)msg{
//    NSLog(@"person send msg:%@",msg);
//}


void sendMessage(id self,SEL _cmd,NSString *msg) {
    NSLog(@"send msg:%@",msg);
}

//動(dòng)態(tài)解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"動(dòng)態(tài)方法解析");
    NSString *methodName = NSStringFromSelector(sel);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
    }
    return NO;
}
@end

image.png

2.如果第一步的動(dòng)態(tài)解析也沒(méi)有找到方法,那么將會(huì)進(jìn)行快速轉(zhuǎn)發(fā),調(diào)用- (id)forwardingTargetForSelector:(SEL)aSelector可以在本類(lèi)方法列表未找到方法的時(shí)候,轉(zhuǎn)發(fā)給其他類(lèi),并返回該類(lèi)對(duì)象:

Child.m 文件實(shí)現(xiàn)了一個(gè)方法

#import "Child.h"

@implementation Child
- (void)sendMessage:(NSString *)msg{
    NSLog(@"child send msg:%@",msg);
}
@end
#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person

//- (void)sendMessage:(NSString *)msg{
//    NSLog(@"person send msg:%@",msg);
//}


//void sendMessage(id self,SEL _cmd,NSString *msg) {
//    NSLog(@"send msg:%@",msg);
//}

//動(dòng)態(tài)解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//    NSLog(@"動(dòng)態(tài)方法解析");
//    NSString *methodName = NSStringFromSelector(sel);
//    if ([methodName isEqualToString:@"sendMessage:"]) {
//        return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
//    }
    return NO;
}

//快速轉(zhuǎn)發(fā)
- (id)forwardingTargetForSelector:(SEL)aSelector{
    NSString *methodName = NSStringFromSelector(aSelector);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        return [Child new];
    }
    return [super forwardingTargetForSelector:aSelector];
}
@end

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


image.png

3.如果沒(méi)有快速轉(zhuǎn)發(fā)也失敗了,那么將會(huì)返回nil,進(jìn)行慢速轉(zhuǎn)發(fā),慢速轉(zhuǎn)發(fā)需要實(shí)現(xiàn)兩個(gè)方法:
(1)- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
(2) - (void)forwardInvocation:(NSInvocation *)anInvocation

#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person

//- (void)sendMessage:(NSString *)msg{
//    NSLog(@"person send msg:%@",msg);
//}


//void sendMessage(id self,SEL _cmd,NSString *msg) {
//    NSLog(@"send msg:%@",msg);
//}

//動(dòng)態(tài)解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//    NSLog(@"動(dòng)態(tài)方法解析");
//    NSString *methodName = NSStringFromSelector(sel);
//    if ([methodName isEqualToString:@"sendMessage:"]) {
//        return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
//    }
    return NO;
}

//快速轉(zhuǎn)發(fā)
- (id)forwardingTargetForSelector:(SEL)aSelector{
//    NSString *methodName = NSStringFromSelector(aSelector);
//    if ([methodName isEqualToString:@"sendMessage:"]) {
//        return [Child new];
//    }
    return [super forwardingTargetForSelector:aSelector];

}

//慢速轉(zhuǎn)發(fā) 1.簽名 2.轉(zhuǎn)發(fā)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSString *methodName = NSStringFromSelector(aSelector);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    SEL sel = anInvocation.selector;
    Child *child = [Child new];
    if ([child respondsToSelector:sel]) {
        [anInvocation invokeWithTarget:child];
    }else {
        [super forwardInvocation:anInvocation];
    }

}

結(jié)果如下,還是轉(zhuǎn)發(fā)給了child的類(lèi)對(duì)象,消息轉(zhuǎn)發(fā)成功:


image.png

4.最后,如果消息轉(zhuǎn)發(fā)失敗,那么將會(huì)報(bào)錯(cuò),但是為了程序防止崩潰,還是有方法可以處理的,因?yàn)榈阶詈笕绻€沒(méi)有轉(zhuǎn)發(fā)成功,那么將會(huì)走-(void)doesNotRecognizeSelector:(SEL)aSelector方法:

#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person

//- (void)sendMessage:(NSString *)msg{
//    NSLog(@"person send msg:%@",msg);
//}


//void sendMessage(id self,SEL _cmd,NSString *msg) {
//    NSLog(@"send msg:%@",msg);
//}

//動(dòng)態(tài)解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//    NSLog(@"動(dòng)態(tài)方法解析");
//    NSString *methodName = NSStringFromSelector(sel);
//    if ([methodName isEqualToString:@"sendMessage:"]) {
//        return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
//    }
    return NO;
}

//快速轉(zhuǎn)發(fā)
- (id)forwardingTargetForSelector:(SEL)aSelector{
//    NSString *methodName = NSStringFromSelector(aSelector);
//    if ([methodName isEqualToString:@"sendMessage:"]) {
//        return [Child new];
//    }
    return [super forwardingTargetForSelector:aSelector];

}

//慢速轉(zhuǎn)發(fā) 1.簽名 2.轉(zhuǎn)發(fā)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSString *methodName = NSStringFromSelector(aSelector);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
//    SEL sel = anInvocation.selector;
//    Child *child = [Child new];
//    if ([child respondsToSelector:sel]) {
//        [anInvocation invokeWithTarget:child];
//    }else {
//        [super forwardInvocation:anInvocation];
//    }
    
    [super forwardInvocation:anInvocation];

}
-(void)doesNotRecognizeSelector:(SEL)aSelector{
    NSLog(@"無(wú)法識(shí)別該消息:%@",NSStringFromSelector(aSelector));
}

@end

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


image.png

到此一個(gè)完整的消息轉(zhuǎn)發(fā)機(jī)制例子就演示完畢了,如果有需要的可以下載完整的源碼,本文也是參考了部分大神的文章,對(duì)自己的一個(gè)小結(jié),如果有誤導(dǎo)的地方,歡迎大家指正以及補(bǔ)充***

?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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