前言
IOS底层原理之动态方法决议 中探究了动态方法决议。在动态决议之后,通过日志辅助功能认识到forwardingTargetForSelector
和methodSignatureForSelector
方法,也就是消息发送的最后一个流程消息转发
准备工作
- objc4-818.2 源码
- CF 源码
- 反汇编工具
Hopper
和ida
消息转发
消息发送在经过动态方法决议仍然没有查找到正真的方法实现,此时动态方法决议抛出imp
= forward_imp
进入消息转发流程。转发流程分两步快速转发
和慢速转发
快速转发
forwardingTargetForSelector
通过日志辅助功能认识到forwardingTargetForSelector
,但是这个方法具体的含义是一无所知的,下面来认识下这个方法。打开Xcode
,command
+ shift
+0
,然后全局搜索forwardingTargetForSelector
forwardingTargetForSelector
含义是返回未识别消息重定向
的对象,简单理解指定一个对象,让这个对象去接收这个消息
实例探究快速转发
首先定义LWPerson
类和LWTest
类,然后在main
函数中调用sayHello
方法。LWPerson
没有实现sayHello
方法,LWTest
类实现sayHello
方法,LWTest
类和LWPerson
类可以不是继承关系
@implementation LWPerson
-(id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(sayHello)) {
return [[LWTest alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
@implementation LWTest
-(void)sayHello{
NSLog(@"---%s---",__func__);
}
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
LWPerson * p = [LWPerson alloc];
[p sayHello];
}
return 0;
}
2021-07-05 17:21:58.201416+0800 objc_msgSend[23131:807070] ----[LWTest sayHello]---
打印结果显示:LWTest
类和LWPerson
类没有任何关系,但是指定给LWTest
类,仍然最后可以查询到,并且没有崩溃消息,其实消息在查询过程中先去跟它关系近
的类中去查找,最后没找到。于是系统把这个权限丢给开发者,你告诉我哪个对象和类能接收这个消息,破罐子破摔了,这我没想到的啊。
如果不给指定的类实现,快速转发也不行了,系统没有底线的给你进行慢速转发,就离谱
慢速转发
慢速转发methodSignatureForSelector
也是消息查找的最后一个流程。给了动态方法决议,给了快速转发,在给你次慢速转发的机会。俗话说事不过三,太过分了系统也忍不了,只能给你个崩溃耗子尾汁
吧
methodSignatureForSelector
的含义是返回一个NSMethodSignature
对象,该对象包含由给定选择器标识的方法的描述。methodSignatureForSelector
一般搭配和forwardInvocation
使用,如果methodSignatureForSelector
方法返回的是一个nil
就不会调用forwardInvocation
@implementation LWPerson
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"---%@---",anInvocation);
}
@end
2021-07-05 21:09:59.688392+0800 objc_msgSend[1121:21900] -[LWPerson sayHello]:
unrecognized selector sent to instance 0x60000001c000
此时methodSignatureForSelector
的返回值是nil
,慢速转发完成,直接奔溃
@implementation LWPerson
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(sayHello)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"---%@---%@",anInvocation.target,NSStringFromSelector(anInvocation.selector));
}
@end
2021-07-05 21:42:47.001759+0800 objc_msgSend[1629:44959] ---<LWPerson: 0x60000000c140>---sayHello
如果methodSignatureForSelector
的返回值是NSMethodSignature
对象,则会调用forwardInvocation
进行实物处理anInvocation
保存了NSMethodSignature
签名信息,还有目标方法的方法签名sel
,以及方法的接收者。此时不会报奔溃信息,当然也可以处理anInvocaion
事务
- (void)forwardInvocation:(NSInvocation *)anInvocation{
LWTest * test = [LWTest alloc];
anInvocation.target = test;
anInvocation.selector = @selector(sayBeautiful);
[anInvocation invoke];
}
2021-07-05 21:52:29.117134+0800 objc_msgSend[1787:51943] ----[LWTest sayBeautiful]---
anInvocation
的target
是[LWTest alloc]
,anInvocation
的selector
是@selector(sayBeautiful)
,[anInvocation invoke]
触发消息的调用。forwardInvocation
方法就像一个不能识别的消息的分发中心,它能够将一个消息翻译成另外一个消息,或者简单的"吃掉“某些消息。所以不处理也不会崩溃
消息换发总结
- 快速转发:通过
forwardingTargetForSelector
实现,如果此时有指定的对象去接收这个消息,就会走之指定对象的查找流程,如果返回是nil
,进入慢速转发流程 - 慢速转发:通过
methodSignatureForSelector
和forwardInvocation
共同实现,如果methodSignatureForSelector
返回值是nil
,慢速查找流程结束,如果有返回值forwardInvocation
的事务处理不处理都不会崩溃
消息转发流程图
反汇编
方法调用奔溃,堆栈信息显示从__forwarding_prep_0___
往下调用最后又调用了doesNotRecognizeSelector
。具体的流程探究下
__forwarding_prep_0___
是属于CoreFoundation
系统库的
下载CoreFoundation
库以后的代码,在源码中全局搜索并无发现,说明这块内容苹果并没有对外提供
Hopper
或者ida
探索
Hopper
反汇编的工具,我们通过反汇编CoreFoundation
的可执行文件,去查找__forwarding_prep_0___
,CoreFoundation
的可执行文件怎么获取
image list
可以获取所有的镜像文件列表,查找到CoreFoundation
的文件路径
全局搜索
__forwarding_prep_0___
,发现只有一个,且会调用__forwarding__
,进入__forwarding__
方法
- 如果
forwardingTargetForSelector
方法没有实现,跳转loc_64ad7
流程 - 如果
forwardingTargetForSelector
方法的返回值是nil
,跳转loc_64ad7
流程
此时进入了慢速转发流程
- 如果
methodSignatureForSelector
没有实现直接跳转到loc_64e47
流程 - 如果
methodSignatureForSelector
返回值等于nil
跳转到loc_64eac
流程 - 如果
methodSignatureForSelector
返回了签名信息的对象
loc_64e47
流程:直接报错跳转到loc_64eac
流程loc_64eac
流程:doesNotRecognizeSelector
崩溃处理
返回签名信息forwardInvocation
方法处理事务
总结
方法的探究基本已经结束,通过探究方法体会到整个探究的思路和方式,这个在我看来是最重要的