说到runtime,就不得不讲到最重要的消息转发机制,转发的流程图如下:
在OC里面,所有的方法调用,在运行时的时候都可以看作是objc_msgSend(obj,@selector())的调用
1.正常的方法调用
person.h文件,声明方法
#import <Foundation/Foundation.h>
@interface Person : NSObject
-(void)sendMessage:(NSString *)msg;
@end
person.m文件
#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person
- (void)sendMessage:(NSString *)msg{
NSLog(@"person send msg:%@",msg);
}
@end
如果person.m实现了-(void)sendMessage:(NSString *)msg 方法,那么将会调用该方法,结果如图:
如果没有实现这个方法,则会报错没有该方法
那么实现消息转发机制的方法,就可以解决这个问题:
- 如果没有实现改方法,会先进行动态解析,调用+ (BOOL)resolveInstanceMethod:(SEL)sel方法
#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person
//- (void)sendMessage:(NSString *)msg{
// NSLog(@"person send msg:%@",msg);
//}
void sendMessage(id self,SEL _cmd,NSString *msg) {
NSLog(@"send msg:%@",msg);
}
//动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"动态方法解析");
NSString *methodName = NSStringFromSelector(sel);
if ([methodName isEqualToString:@"sendMessage:"]) {
return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
}
return NO;
}
@end
2.如果第一步的动态解析也没有找到方法,那么将会进行快速转发,调用- (id)forwardingTargetForSelector:(SEL)aSelector可以在本类方法列表未找到方法的时候,转发给其他类,并返回该类对象:
Child.m 文件实现了一个方法
#import "Child.h"
@implementation Child
- (void)sendMessage:(NSString *)msg{
NSLog(@"child send msg:%@",msg);
}
@end
#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person
//- (void)sendMessage:(NSString *)msg{
// NSLog(@"person send msg:%@",msg);
//}
//void sendMessage(id self,SEL _cmd,NSString *msg) {
// NSLog(@"send msg:%@",msg);
//}
//动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
// NSLog(@"动态方法解析");
// NSString *methodName = NSStringFromSelector(sel);
// if ([methodName isEqualToString:@"sendMessage:"]) {
// return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
// }
return NO;
}
//快速转发
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [Child new];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
运行结果:
3.如果没有快速转发也失败了,那么将会返回nil,进行慢速转发,慢速转发需要实现两个方法:
(1)- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
(2) - (void)forwardInvocation:(NSInvocation *)anInvocation
#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person
//- (void)sendMessage:(NSString *)msg{
// NSLog(@"person send msg:%@",msg);
//}
//void sendMessage(id self,SEL _cmd,NSString *msg) {
// NSLog(@"send msg:%@",msg);
//}
//动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
// NSLog(@"动态方法解析");
// NSString *methodName = NSStringFromSelector(sel);
// if ([methodName isEqualToString:@"sendMessage:"]) {
// return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
// }
return NO;
}
//快速转发
- (id)forwardingTargetForSelector:(SEL)aSelector{
// NSString *methodName = NSStringFromSelector(aSelector);
// if ([methodName isEqualToString:@"sendMessage:"]) {
// return [Child new];
// }
return [super forwardingTargetForSelector:aSelector];
}
//慢速转发 1.签名 2.转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
SEL sel = anInvocation.selector;
Child *child = [Child new];
if ([child respondsToSelector:sel]) {
[anInvocation invokeWithTarget:child];
}else {
[super forwardInvocation:anInvocation];
}
}
结果如下,还是转发给了child的类对象,消息转发成功:
4.最后,如果消息转发失败,那么将会报错,但是为了程序防止崩溃,还是有方法可以处理的,因为到最后如果还没有转发成功,那么将会走-(void)doesNotRecognizeSelector:(SEL)aSelector方法:
#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person
//- (void)sendMessage:(NSString *)msg{
// NSLog(@"person send msg:%@",msg);
//}
//void sendMessage(id self,SEL _cmd,NSString *msg) {
// NSLog(@"send msg:%@",msg);
//}
//动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
// NSLog(@"动态方法解析");
// NSString *methodName = NSStringFromSelector(sel);
// if ([methodName isEqualToString:@"sendMessage:"]) {
// return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
// }
return NO;
}
//快速转发
- (id)forwardingTargetForSelector:(SEL)aSelector{
// NSString *methodName = NSStringFromSelector(aSelector);
// if ([methodName isEqualToString:@"sendMessage:"]) {
// return [Child new];
// }
return [super forwardingTargetForSelector:aSelector];
}
//慢速转发 1.签名 2.转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
// SEL sel = anInvocation.selector;
// Child *child = [Child new];
// if ([child respondsToSelector:sel]) {
// [anInvocation invokeWithTarget:child];
// }else {
// [super forwardInvocation:anInvocation];
// }
[super forwardInvocation:anInvocation];
}
-(void)doesNotRecognizeSelector:(SEL)aSelector{
NSLog(@"无法识别该消息:%@",NSStringFromSelector(aSelector));
}
@end
运行结果:
到此一个完整的消息转发机制例子就演示完毕了,如果有需要的可以下载完整的源码,本文也是参考了部分大神的文章,对自己的一个小结,如果有误导的地方,欢迎大家指正以及补充***