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;
}
那么我们就写个栗子🌰来看看,消息转发会调用哪些方法。
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:
方法之后会调用forwardingTargetForSelector
、methodSignatureForSelector:
、resolveInstanceMethod:
、 doesNotRecognizeSelector:
等方法。那么下面就一一探索下。
2. 快速转发
从Xcode
的帮助文档可以搜索到forwardingTargetForSelector
方法的定义,和用法。
- forwardingTargetForSelector定义
大概意思就是:返回一个无法识别的消息定向到的对象。 说白了就是,找一个接受者,也可以理解成“背锅侠”,哈哈!
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
return [JPStudent alloc];
}
- forwardingTargetForSelector 代码实现
当JPPerson
类中没有实现sayHello
方法,直接调用[person sayHello]
肯定会奔溃的,但是现在找了一个背锅的,就让背锅侠去处理吧。
如果
运行时
在消息转发的第一步
中未找到所调用方法的实现,那么当前接收者还有第二次
机会进行处理。这时运行期系统会调用-(id)forwardingTargetForSelector:(SEL)aSelector
方法,该方法可以返回一个能处理的对象,运行时系统会根据返回的对象进行查找,若找到则跳转到相应方法的实现,则消息转发结束。如果没有找到,则返回nil
,继续下面的操作。
注意
这里不要返回self
,否则会死循环!
3. 慢速转发
如果快速转发没有背锅侠,程序员不处理就会进入慢速转发流程。
3.1 方法签名
- 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 定义
上面的方法签名的信息会保存在
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默认抛出异常信息
-
消息转发流程图
更多内容持续更新
🌹 喜欢就点个赞吧👍🌹
🌹 觉得学习到了的,可以来一波,收藏+关注,评论 + 转发,以免你下次找不到我😁🌹
🌹欢迎大家留言交流,批评指正,互相学习😁,提升自我🌹