一、消息发送
回顾前一篇文章,我们总结了objc_msgSend第一阶段~消息发送的流程图,如下:
二、动态解析
2.1 动态解析流程探索
- 判断是否有动态解析
LOOKUP_RESOLVER
标记
- 判断是不是
Meta-Class
,非元类调用resolveInstanceMethod
、元类调用resolveClassMethod
- 判断该方法是否已经被标记有动态解析
behavior
,标记后receiverClass
的cache
中也会缓存标记
2.2 动态解析流程代码验证
2.2.1 消息发送流程代码实例
receiver(person)
通过isa
找到receiverClass(ZGPerson.class)
,在receiverClass
的class_rw_t
中查找方法test
,查找到test
方法,调用方法,结束查找
并将方法缓存到reveiverClass
的cache
中。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ZGPerson : NSObject
- (void)test;
@end
NS_ASSUME_NONNULL_END
#import "ZGPerson.h"
@implementation ZGPerson
- (void)test {
NSLog(@"%s",__func__);
}
@end
#import <Foundation/Foundation.h>
#import "ZGPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZGPerson *person = [ZGPerson new];
[person test];
}
return 0;
}
2.2.2 动态解析流程代码实例
- 屏蔽掉
- (void)test
的实现,因为它是实例方法,并且ZGPerson
不是Meta-class
,所以我们调用resolveInstanceMethod
方法来进入动态解析阶段。
#import "ZGPerson.h"
@implementation ZGPerson
//- (void)test {
// NSLog(@"%s",__func__);
//}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"%s",__func__);
return [super resolveInstanceMethod:sel];
}
@end
- 在动态解析阶段,我们首先调用父类方法,什么操作都不做,看一下确实执行并调用了
resolveInstanceMethod
方法,但是因为动态解析阶段没有查找到对应的方法,产生崩溃。
- 动态添加方法,进入动态解析阶段,调用
resolveInstanceMethod
方法,该方法中将test
的实现动态替换为other
,所以我们查找到other
方法,调用方法,结束查找并将方法缓存到reveiverClass
的cache
中。
#import <objc/runtime.h>
@implementation ZGPerson
//- (void)test {
// NSLog(@"%s",__func__);
//}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
//获取其他方法
Method method = class_getInstanceMethod(self, @selector(other));
//动态添加test方法实现
class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
//告诉系统有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
- (void)other {
NSLog(@"%s",__func__);
}
@end
2.3 动态解析流程图
通过前面部分源码流程探索和代码验证,我们可以得到以下动态解析的流程图:
三、消息转发
3.1 消息转发流程探索
log_and_fill_cache
==>objcMsgLogEnabled
logMessageSend
,暂存数据地址/tmp/msgSends-%d
instrumentObjcMessageSends
- 实例代码添加
instrumentObjcMessageSends
方法配合验证
- 在电脑上前往文件夹/tmp找到msgSends文件
- 方法调用堆栈
forwardingTargetForSelector:
和methodSignatureForSelector:
就是我们消息转发调用的方法
3.2 消息转发流程代码验证
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ZGPerson : NSObject
- (void)test;
@end
NS_ASSUME_NONNULL_END
#import "ZGPerson.h"
#import <objc/runtime.h>
#import "ZGCat.h"
@implementation ZGPerson
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return nil;
}
return [super forwardingTargetForSelector:aSelector];
}
//方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
}
return [super methodSignatureForSelector:aSelector];
}
// NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
// anInvocation.target 方法调用者
// anInvocation.selector 方法名
// anInvocation getArgument:<#(nonnull void *)#> atIndex:<#(NSInteger)#>
- (void)forwardInvocation:(NSInvocation *)anInvocation {
// anInvocation.target = [ZGCat new];
// [anInvocation invoke];
[anInvocation invokeWithTarget:ZGCat.new];
}
@end
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ZGCat : NSObject
- (void)test;
@end
NS_ASSUME_NONNULL_END
#import "ZGCat.h"
@implementation ZGCat
- (void)test {
NSLog(@"%s",__func__);
}
@end
#import <Foundation/Foundation.h>
#import "ZGPerson.h"
#import "ZGCat.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZGPerson *person = [ZGPerson new];
[person test];
}
return 0;
}
3.3 消息转发流程图
通过前面部分源码流程探索和代码验证,我们可以得到以下消息转发的流程图: