先来看一张图:
从全局来看,消息转发机制共分为3大步骤:
1.Method resolution 方法解析处理阶段
2.fast forwarding 快速转发阶段
3.Normal forwarding 常规转发阶段
抛出unrecognized selector 的报错,也就是需要从这3步里面.
第一步:我们称之为方法解析:
方法解析的含义就是:当我们的类发现我们调用的方法 unrecognize 的时候就会 调用第一个方法
// 类方法专用
+ (BOOL)resolveClassMethod:(SEL)sel
// 对象方法专用
+ (BOOL)resolveInstanceMethod:(SEL)sel
这个方法来询问当前对象是否能够处理sel,如果能那么return YES,在这个时候我们就可以通过 class_addMethod 来动态添加方法.让我们自己的类响应这个方法.这里为第一步的消息转发.如果我们的类的有这个方法也不会走到这里,很遗憾我们进入到第二步的转发:
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *method = NSStringFromSelector(sel);
if ([@"playPiano" isEqualToString:method]) {
/**
添加方法
@param self 调用该方法的对象
@param sel 选择子
@param IMP 新添加的方法,是c语言实现的
@param 新添加的方法的类型,包含函数的返回值以及参数内容类型,eg:void xxx(NSString *name, int size),类型为:v@i
*/
class_addMethod(self, sel, (IMP)playPiano, "v");
return YES;
}
return NO;
}
第二步:称之为 "备援接受者"
意思就是:当前的类不能够实现这个sel,但是检查是否有备胎可以实现. "fuck 很痛苦!!!!!!!!!!"
- (id)forwardingTargetForSelector:(SEL)aSelector
这个方法就是消息转发的第二步:来找可以实现该方法的对象.
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSString *seletorString = NSStringFromSelector(aSelector);
if ([@"playPiano" isEqualToString:seletorString]) {
Student *s = [[Student alloc] init];
return s;
}
// 继续转发
return [super forwardingTargetForSelector:aSelector];
}
我们的teacher 类不能实现 playPanio的方法 ,消息转发给我的学生来实现.直接将该消息转给student类. 但最后我们的程序崩溃了,说明比较悲哀,连备胎都没有. 那我们就进入最后一步啦,还是将消息转发给其他的类.这里不仅可以转给其他的类,而且可以调用其他的方法. 比如:我调用:[Teacher playPaino] 方法,进过最后的消息转发,我调用的 student类的 travel 方法. 但是很少有人会在这一步做处理,我们知道方法越靠后,需要的东西就越多,资源开销就比较大,所以在这种情况下,一般用来增加参数,或者改变选择子.
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation
第三步: 首先获取当前的方法签名: Signature
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSString *method = NSStringFromSelector(aSelector);
if ([@"playPiano" isEqualToString:method]) {
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
return signature;
}
return nil;
}
然后通过:
- (void)forwardInvocation:(NSInvocation *)anInvocation
进行消息转发:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL sel = @selector(travel:);
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
anInvocation = [NSInvocation invocationWithMethodSignature:signature];
[anInvocation setTarget:self];
[anInvocation setSelector:@selector(travel:)];
NSString *city = @"北京";
// 消息的第一个参数是self,第二个参数是选择子,所以"北京"是第三个参数
[anInvocation setArgument:&city atIndex:2];
if ([self respondsToSelector:sel]) {
[anInvocation invokeWithTarget:self];
return;
} else {
Student *s = [[Student alloc] init];
if ([s respondsToSelector:sel]) {
[anInvocation invokeWithTarget:s];
return;
}
}
// 从继承树中查找
[super forwardInvocation:anInvocation];
}
如果当前类实现了 travel 方法 那你调用 playPaino的方法就不会崩溃, 如果没实现,但 student 实现了 也不会崩溃.
崩溃的过程大致就是这三步,最后一步还是不能找到实现方法,那就抛出异常,dose not recognize Selector
注意:
类方法需要添加到元类里面,oc中所有的类本质上来说都是对象,对象的ISA指针指向本类,类的isa指针指向元类.元类的isa指针指向根源类,根源类的isa指针指向自己,这样的话就形成了一个闭环.