iOS - Runtime-消息机制-objc_msgSend()
前言
本章主要介绍消息机制-objc_msgSend的执行流程,分为消息发送、动态方法解析、消息转发三个阶段,每个阶段可以做什么。还介绍了super的本质是什么,如何调用的
1. objc_msgSend执行流程
OC中的方法调用,其实都是转换为objc_msgSend函数的调用
objc_msgSend的执行流程可以分为3大阶段
消息发送
动态方法解析
消息转发
1.1 消息发送

如果是从
class_rw_t中查找方法- 已经排序的,二分查找
- 没有排序的,遍历查找
receiver通过isa指针找到receiverClass
receiverClass通过superclass指针找到superClass
1.2 动态方法解析

1.2.1 开发者可以实现以下方法,来动态添加方法实现
- +resolveInstanceMethod: -----用于
实例方法 - +resolveClassMethod: -----用于
类方法
1.2.2 动态解析过后,会重新走“消息发送”的流程
- “从receiverClass的cache中查找方法”这一步开始执行
1.2.3 示例
ZSXPerson类有test方法,但是方法实现注释掉了
@interface ZSXPerson : NSObject- (void)test;@end@implementation ZSXPerson//- (void)test {
// NSLog(@"ZSXPerson - %s", __func__);
//}@end
此时我们调用 test 方法发就会报错unrecognized selector sent to instance 
在动态方法解析阶段给类对象增加方法实现
- (void)other {NSLog(@"ZSXPerson - %s", __func__);
}+ (BOOL)resolveInstanceMethod:(SEL)sel {if (sel == @selector(test)) {// 获取 qitafangfaMethod method = class_getInstanceMethod(self, @selector(other));// 动态添加 test 方法的实现class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));// 返回YES代表有动态添加方法return YES;}return [super resolveInstanceMethod:sel];
}
将方法实现other设置为test的方法实现,这时候可以看到不会报错了,而是执行了- (void)other方法 
1.2.3.1 类方法
动态方法解析类方法也是类似的,只不过用的是+resolveClassMethod:方法,并且class_addMethod应该给元类对象添加方法。使用object_getClass(self)获取元类对象
@interface ZSXPerson : NSObject- (void)test;+ (void)test1;@end@implementation ZSXPerson//- (void)test {
// NSLog(@"ZSXPerson - %s", __func__);
//}//+ (void)test1 {
// NSLog(@"ZSXPerson - %s", __func__);
//}- (void)other {NSLog(@"ZSXPerson - %s", __func__);
}+ (BOOL)resolveInstanceMethod:(SEL)sel {if (sel == @selector(test)) {// 获取 qitafangfaMethod method = class_getInstanceMethod(self, @selector(other));// 动态添加 test 方法的实现class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));// 返回YES代表有动态添加方法return YES;}return [super resolveInstanceMethod:sel];
}+ (BOOL)resolveClassMethod:(SEL)sel {if (sel == @selector(test1)) {// 获取 qitafangfaMethod method = class_getInstanceMethod(self, @selector(other));// 动态添加 test 方法的实现class_addMethod(object_getClass(self), sel, method_getImplementation(method), method_getTypeEncoding(method));// 返回YES代表有动态添加方法return YES;}return [super resolveClassMethod:sel];
}@end
main.m
int main(int argc, const char * argv[]) {@autoreleasepool {
// ZSXPerson *person = [[ZSXPerson alloc] init];
// [person test];[ZSXPerson test1];}return 0;
}
执行结果 
1.3 消息转发

如果动态方法解析阶段没有处理,回来到消息转发阶段
- 首先来到
forwardingTargetForSelector:方法,该方法中可以重新返回一个消息接收者,程序将会重新执行objc_msgSend()方法,此时消息时发送给新的接受者 - 如果
forwardingTargetForSelector:方法没有处理,会来到methodSignatureForSelector:方法,该方法可以返回一个方法签名,返回后,程序会继续调用forwardInvocation:方法。如果methodSignatureForSelector:方法也没处理,程序就抛出异常 - 在
forwardInvocation:方法中,开发者可以自定义任何逻辑 - 以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)
1.3.1 forwardingTargetForSelector:
新建一个ZSXCat类,该类实现了- (void)test方法
@interface ZSXCat : NSObject@end@implementation ZSXCat- (void)test {NSLog(@"ZSXCat - %s", __func__);
}@end
实现forwardingTargetForSelector:方法,将消息接受者转发给ZSXCat对象
@interface ZSXPerson : NSObject- (void)test;@end@implementation ZSXPerson//- (void)test {
// NSLog(@"ZSXPerson - %s", __func__);
//}//+ (void)test1 {
// NSLog(@"ZSXPerson - %s", __func__);
//}- (id)forwardingTargetForSelector:(SEL)aSelector {if (aSelector == @selector(test)) {return [[ZSXCat alloc] init];}return [super forwardingTargetForSelector:aSelector];
}@end
运行结果:
调用了ZSXCat的- (void)test方法
1.3.2 methodSignatureForSelector:
@implementation ZSXPerson//- (void)test {
// NSLog(@"ZSXPerson - %s", __func__);
//}//- (id)forwardingTargetForSelector:(SEL)aSelector {
// if (aSelector == @selector(test)) {
// return [[ZSXCat alloc] init];
// }
// 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:NULL atIndex:0] 参数
- (void)forwardInvocation:(NSInvocation *)anInvocation {NSLog(@"---- forwardInvocation");
}@end
返回方法签名,来到forwardInvocation:继续执行 
2. 拓展
2.1 forwardInvocation:自定义逻辑
在动态方法解析阶段,+resolveClassMethod:方法是可以给消息接受者动态增加一个`方法实现
在消息转发阶段,forwardingTargetForSelector:方法是可以重新返回一个消息接受者,相当于是让另一个人来处理这个方法。
但是,来到methodSignatureForSelector:方法后,可以使用方法签名自定义更复杂的业务
2.1.1 方法签名阶段的其他用法
把方法调用 转发给ZSXCat对象
- (void)forwardInvocation:(NSInvocation *)anInvocation {NSLog(@"---- forwardInvocation");// 把方法调用 转发给ZSXCat对象[anInvocation invokeWithTarget:[[ZSXCat alloc]init]];
}
使用参数
方法增加age参数- (void)test:(int)age
调用时传入参数:
int main(int argc, const char * argv[]) {@autoreleasepool {ZSXPerson *person = [[ZSXPerson alloc] init];[person test:10];}return 0;
}
方法签名使用参数
// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if (aSelector == @selector(test:)) {return [NSMethodSignature signatureWithObjCTypes:"i24@0:8i16"];}return [super methodSignatureForSelector:aSelector];
}// NSInvocation封装了一个方法调用,包括:方法调用者、方法签名、方法参数
// anInvocation.target 方法调用者
// anInvocation.selector 方法名
// [anInvocation getArgument:NULL atIndex:0] 参数
- (void)forwardInvocation:(NSInvocation *)anInvocation {NSLog(@"---- forwardInvocation");// 把方法调用 转发给ZSXCat对象
// [anInvocation invokeWithTarget:[[ZSXCat alloc]init]];// 取出参数。参数顺序:receiver、selector、other argumentsint age;[anInvocation getArgument:&age atIndex:2];NSLog(@"age is %d", age + 10);
}
打印结果: 
2.2 消息转发 - 类方法
在处理消息转发的时候,会发现如果forwardingTargetForSelector和methodSignatureForSelector方法,使用+开头写法时代码提示没有这俩方法,所以有的人认为消息转发不支持类方案
其实是支持的,把方法的-号改成+即可:
@interface ZSXPerson : NSObject- (void)test:(int)age;+ (void)test1;@end
@implementation ZSXCat- (void)test {NSLog(@"ZSXCat - %s", __func__);
}+ (void)test1 {NSLog(@"ZSXCat - %s", __func__);
}@end
main.m
int main(int argc, const char * argv[]) {@autoreleasepool {
// ZSXPerson *person = [[ZSXPerson alloc] init];
// [person test:10];[ZSXPerson test1];}return 0;
}
ZSXPerson.m
@implementation ZSXPerson//- (void)test:(int)age {
// NSLog(@"ZSXPerson - %s", __func__);
//}- (id)forwardingTargetForSelector:(SEL)aSelector {if (aSelector == @selector(test)) {return [[ZSXCat alloc] init];}return [super forwardingTargetForSelector:aSelector];
}//+ (id)forwardingTargetForSelector:(SEL)aSelector {
// if (aSelector == @selector(test1)) {
// return [ZSXCat class];
// }
// return [super forwardingTargetForSelector:aSelector];
//}// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if (aSelector == @selector(test:)) {return [NSMethodSignature signatureWithObjCTypes:"i24@0:8i16"];}return [super methodSignatureForSelector:aSelector];
}+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if (aSelector == @selector(test1)) {return [NSMethodSignature signatureWithObjCTypes:"v@:"]; //v16@0:8 可简写为 v@:}return [super methodSignatureForSelector:aSelector];
}// NSInvocation封装了一个方法调用,包括:方法调用者、方法签名、方法参数
// anInvocation.target 方法调用者
// anInvocation.selector 方法名
// [anInvocation getArgument:NULL atIndex:0] 参数
- (void)forwardInvocation:(NSInvocation *)anInvocation {NSLog(@"---- forwardInvocation");
// // 把方法调用 转发给ZSXCat对象
// [anInvocation invokeWithTarget:[[ZSXCat alloc]init]];// 取出参数。参数顺序:receiver、selector、other arguments
// int age;
// [anInvocation getArgument:&age atIndex:2];
// NSLog(@"age is %d", age + 10);
}+ (void)forwardInvocation:(NSInvocation *)anInvocation {NSLog(@"---- forwardInvocation");// 把方法调用 转发给ZSXCat对象[anInvocation invokeWithTarget:[ZSXCat class]];
}@end
2.3 super
2.3.1 示例代码
ZSXStudent继承于ZSXPerson,ZSXPerson继承于NSObject。如下代码打印结果是什么
ZSXPerson.h
@interface ZSXPerson : NSObject@end
ZSXStudent.h
@interface ZSXStudent : ZSXPerson@end
ZSXStudent.m
@implementation ZSXStudent- (instancetype)init {if (self = [super init]) {NSLog(@"[self class] - %@", [self class]);NSLog(@"[self superclass] - %@", [self superclass]);NSLog(@"--------------------------------");NSLog(@"[super class] - %@", [super class]);NSLog(@"[super superclass] - %@", [super superclass]);}return self;
}@end
打印结果:
示例中,比较容易混淆的是[super class]、[super superclass],他们的打印结果和[self class]、[self superclass]是一样的。
2.3.2 super本质
super调用时,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数
- struct objc_super
- SEL
objc_super结构体: 
receiver是消息接收者super_class是第一个要搜索的类
super与self相比,它们消息接受者都是self,super会多传一个super_class,表示从super_class开始查找。
2.3.3 分析
ZSXStudent中,执行[super class],相当于执行这句:
objc_msgSendSuper({self, [ZSXPerson class]}, @selector(class));
表示:
- 消息接收者:
self - 从
ZSXPerson类对象开始 - 查找调用
class方法
class方法存在于NSObject中的,此时不管从ZSXStudent开始查找,还是从ZSXPerson开始查找,最终都到NSObject才找到方法
class方法实现如下: 
[super class]和[self class]他们的消息接受者都是self,也就是ZSXStudent,因此他们打印结果都是ZSXStudent。superclass则都是ZSXPerson
@oubijiexi