前言
在上一篇动态方法决议中中,从消息慢速查找没有找到就会进入动态决议
,如果动态决议没有处理,通过崩溃日志可以看到forwardingTargetForSelector
和methodSignatureForSelector
也就是消息查找的最后流程消息转发
消息快速转发 和 消息慢速转发
forwardingTargetForSelector
在源码中只有申明,没有具体的方法, 在NSObject文档中也只是说明,如果方法执行不了,可以转发给别人来处理- 当
forwardingTargetForSelector
后面还有个methodSignatureForSelector
方法,也就是快速转发没找到,就会进入methodSignatureForSelector
方法,文档上说,这个方法要返回一个方法签名NSMethodSignature
,而且在消息的转发期间,必须要创建NSInvocation
,也就是实现forwardInvocation
方法
代码验证
@implementation ZMPerson
// 正常的消息转发
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString * methodName = NSStringFromSelector(sel);
if ([methodName isEqualToString:@"sendMessage:"]) {
return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
}
return NO;
}
// 快速转发
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSString * methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [ZMStudent new];
}
return [super forwardingTargetForSelector:aSelector];
}
// 1. 方法签名
// 2. 消息转发
- (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];
ZMStudent * tempObj = [ZMStudent new];
if ([tempObj respondsToSelector:sel]) {
[anInvocation invokeWithTarget:tempObj];
}else {
[super forwardInvocation:anInvocation];
}
}
- (void)doesNotRecognizeSelector:(SEL)aSelector {
NSLog(@"消息转发失败");
}
总结
- 快速转发:调用
forwardingTargetForSelector
实现快速转发流程,可以返回自己想要替自己实现的对象去实现该方法,如果返回nil
将进入慢速转发
流程。 - 慢速转发:慢速转发需要调用
methodSignatureForSelector
和forwardInvocation
共同实现,methodSignatureForSelector
需要返回方法签名,而forwardInvocation
里面可以做一些处理,例如指定对象处理,指定方法等。如果 没有方法签名,则结束慢速查找。
消息转发图解
消息转发汇编
- 假如这些转发流程都没有处理,运行肯定是报错的,那么怎么查看呢,我们可以用
bt命令
打印堆栈:
通过分析得知,在
doesNotRecognizeSelector
之后还走两个方法___forwarding___
和__forwarding_prep_0___
方法,这两个方法都在CoreFoundation
库中,按照以往的经验去源码中分析下作用,但是源码中并没有找到
,说明CoreFoundation
库并没有完全开源
反汇编 Hppper
将找到的CoreFoundation
动态库拖到Hopper
中
- 然后点击左侧
label
搜索__forwarding_prep_0___
,主要查看伪代码
- 我们看到
__forwarding_prep_0___
中调用___forwarding___
后得到rax
,双击进入___forwarding___
方法:
- 这里显示判断
forwardingTargetForSelector
有没有实现,如果没有实现就会走loc_64ad7
,也就是慢速转发:
- 在慢速转发时,先判断是不是僵尸对象
_NSZombie_
,再判断methodSignatureForSelector
是否实现,没有实现就走loc_64e47
- 如果
methodSignatureForSelector
返回值为nil
,则走loc_64eac
,也就是报错 - 如果
methodSignatureForSelector
有返回值,则会去判断forwardStackInvocation
方法是否存在,然后进行后面的处理