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)) {
// 获取 qitafangfa
Method 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)) {
// 获取 qitafangfa
Method 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)) {
// 获取 qitafangfa
Method 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 arguments
int 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