Objective-C中的方法转成消息发送后,会在相关的类对象中搜索方法列表,如果找不到则会沿着继承树向上一直搜索直到NSObject,如果找不到就执行doesNotRecongnizeSelector方法报unrecognizes selector。
但是在报错之前,有三次处理问题的机会。
1.动态方法分析
2.备用接收者
3.完整消息转发
消息转发.png
动态方法分析
Objective-C运行时会调用+resolveInstanceMethod:或者+resloveClassMethod:,提供一个函数实现。如果返回YES,那运行时系统会重新启动一次消息发送过程。
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:@selector(func:)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if(sel == @selectot(func:)) {
class_addMethod([self class], sel, (IMP)funcMethod, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void funcMethod(id obj, SEL _cmd) {
NSLog(@"Action");
}
可以看到虽然没有实现func:方法,但是我们通过class_addMethod动态添加了funcMethod方法,并执行funcMethod这个方法的IMP。
如果resolve方法返回NO,运行时就会移到下一步:forwardingTargetForSelector。
备用接收者
如果目标对象实现了-forwardingTargetForSelector:,Runtime这时会调用这个方法,把消息转发给其他对象。
#import "objc/runtime.h"
@interface Object: NSObject
@end
@implementation Object
- (void)func {
NSLog(@"func action!");
}
@end
#import "objc/runtime.h"
@interface ViewController: NSObject
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:@selector(func)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
return NO;
}
- (id)forwardingTargetForSelector:(SEL)sel {
if (sel == @selector(func)) {
return [Object new];
}
return [super forwardingTargetForSelector:sel];
}
@end
通过forwardingTargetForSelector把当前ViewController的方法转发给Object执行。
完整消息转发
如果前两步都不能处理未知消息,则唯一能做的就是启用完整的消息转发机制了。
首先发送-methodSignatureForSelector:消息获取函数的参数及返回值类型。如果-methodSignatureForSelector:返回nil,Runtime会发出-doesNotRecognizeSelector:消息,程序这时也就挂了。如果返回一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。
#import "objc/runtime.h"
@interface Object: NSObject
@end
@implementation Object
- (void)func {
NSLog(@"func action!");
}
@end
#import "objc/runtime.h"
@interface ViewController: NSObject
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:@selector(func)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
return NO;
}
- (id)forwardingTargetForSelector:(SEL)sel {
return nil;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
if ([NSStringFromSelector(sel) isEqualToString:@"func"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"]; // 签名,进入forwardInvocation
}
return [super methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
SEL sel = invocation.selector;
Object *obj = [Object new];
if ([obj respondsToSelector:sel]) {
[invocation invokeWithTarget:obj];
}else {
[self doesNotRecognizeSelector:sel];
}
}
@end
通过签名,Runtime生成了一个对象,发送forwardInvocation,方法里面让Object对象执行func方法。
以上就是Runtime的三次转发流程。
网友评论