iOS底层探索之Runtime(五): 消息转发

50 篇文章 6 订阅
7 篇文章 1 订阅

1.回顾

在上篇博文iOS底层探索之Runtime(四): 动态方法解析已经分析了动态方法解析阶段,本次内容主要对消息发送的第三个阶段——>消息转发进行分析。

动态方法解析,不处理的话,就会进入消息转发流程,那么消息转发流程会调用哪些方法呢?

偶然间,在苹果的源码里面发现了一个神奇的方法instrumentObjcMessageSends,这是打印日志的一个方法。

void instrumentObjcMessageSends(BOOL flag)
{
    bool enable = flag;

    // Shortcut NOP
    if (objcMsgLogEnabled == enable)
        return;

    // If enabling, flush all method caches so we get some traces
    if (enable)
        _objc_flush_caches(Nil);

    // Sync our log file
    if (objcMsgLogFD != -1)
        fsync (objcMsgLogFD);

    objcMsgLogEnabled = enable;
}

logMessageSend
那么我们就写个栗子🌰来看看,消息转发会调用哪些方法。

extern void instrumentObjcMessageSends(BOOL flag);

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        instrumentObjcMessageSends(YES);
        LGPerson *person = [LGPerson alloc];
        [person sayInstanceMethod];
        instrumentObjcMessageSends(NO);
        NSLog(@"Hello, World!");
    }
    return 0;
}

在Mac 访达-> 前往 -> 输入/tmp/msgSends打开文件目录

前往

双击打开生成的msgSends-5425日志文件

在这里插入图片描述
从日志文件可以发现,在动态方法解析调用了resolveInstanceMethod:方法之后会调用forwardingTargetForSelectormethodSignatureForSelector:resolveInstanceMethod:doesNotRecognizeSelector:等方法。那么下面就一一探索下。

2. 快速转发

Xcode的帮助文档可以搜索到forwardingTargetForSelector方法的定义,和用法。

  • forwardingTargetForSelector定义

forwardingTargetForSelector

大概意思就是:返回一个无法识别的消息定向到的对象。 说白了就是,找一个接受者,也可以理解成“背锅侠”,哈哈!

背锅侠

- (id)forwardingTargetForSelector:(SEL)aSelector{
	NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
	return [JPStudent alloc];
}
  • forwardingTargetForSelector 代码实现
    forwardingTargetForSelector
    JPPerson 类中没有实现sayHello方法,直接调用[person sayHello]肯定会奔溃的,但是现在找了一个背锅的,就让背锅侠去处理吧。

如果运行时在消息转发的第一步中未找到所调用方法的实现,那么当前接收者还有第二次机会进行处理。这时运行期系统会调用-(id)forwardingTargetForSelector:(SEL)aSelector 方法,该方法可以返回一个能处理的对象,运行时系统会根据返回的对象进行查找,若找到则跳转到相应方法的实现,则消息转发结束。如果没有找到,则返回nil,继续下面的操作。

注意这里不要返回self,否则会死循环!

3. 慢速转发

如果快速转发没有背锅侠,程序员不处理就会进入慢速转发流程。

这个锅不背了

3.1 方法签名

  • methodSignatureForSelector定义
    methodSignatureForSelector

意思是:返回一个 NSMethodSignature对象,该对象包含由给定选择器标识的方法的描述。 简单来说就是方法签名methodSignatureForSelector必须和forwardInvocation配合使用

	 慢速转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
	NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
	if (aSelector == @selector(sayHello)) {
		return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
	}
	return [super methodSignatureForSelector:aSelector];
}

3.2 消息转发

  • forwardInvocation 定义
    forwardInvocation

上面的方法签名的信息会保存在NSInvocation中,通过 SEL sel = [anInvocation selector] 获取方法编号

- (void)forwardInvocation:(NSInvocation *)anInvocation{
		//    NSLog(@"%@ - %@",anInvocation.target,NSStringFromSelector(anInvocation.selector));
	JPStudent *s = [JPStudent alloc];
	if ([self respondsToSelector:anInvocation.selector]) {
		[anInvocation invoke];
	}else if ([s respondsToSelector:anInvocation.selector]){
		[anInvocation invokeWithTarget:s];
	}else{
		NSLog(@"%s - %@",__func__,NSStringFromSelector(anInvocation.selector));
	}
}

4. 消息不处理

如果以上方法都没有实现,会执行-(void)doesNotRecognizeSelector:(SEL)aSelector这个方法

默认这里会抛出异常,也可以自己打印抛出异常信息,但是程序还是会奔溃的!

//如果以上流程都不处理,找不到任何消息处理者,就执行这个
-(void)doesNotRecognizeSelector:(SEL)aSelector {
	NSLog(@"找不到方法");
}

实在是背不动了

5. 总结

  • 动态方法解析不处理,会进入消息转发流程

  • 消息转发流程有快速转发慢速转发

  • 如果消息转发阶段,快速转发和慢速转发不处理,就进入doesNotRecognizeSelector默认抛出异常信息

  • 消息转发流程图
    消息转发流程图

更多内容持续更新

🌹 喜欢就点个赞吧👍🌹

🌹 觉得学习到了的,可以来一波,收藏+关注,评论 + 转发,以免你下次找不到我😁🌹

🌹欢迎大家留言交流,批评指正,互相学习😁,提升自我🌹

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡卡西Sensei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值