消息转发机制

2018-11-22  本文已影响0人  CharmecarWang

在OC方法调用过程当中,当无法响应一个selector时,会触发消息转发机制。
在触发消息转发机制之前,Runtime提供了两个轻量的方法来处理这个selector

resolveInstanceMethod

该方法主要是当前类为无法响应的selector提供方法实现(IMP)的机会。

void dynamicMethodIMP(id self, SEL _cmd){/*...implementation...*/}
+ (BOOL) resolveInstanceMethod:(SEL)aSEL
{
    if (aSEL == @selector(resolveThisMethodDynamically))
    {
          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
          return YES;
    }
    return [super resolveInstanceMethod:aSel];
}

如果该selector是类方法,则使用resolveClassMethod

如果resolveInstanceMethod返回NO,则说明该类无法为这个selector提供方法实现,此时将会调用forwordingTargetForSelector

forwordingTargetForSelector

这个方法简单的将selector转发给其他对象来处理。

-(id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(dynamicSelector) && [self.myObj respondsToSelector:@selector(dynamicSelector)]) {
        return self.myObj;
    }else{
        return [super forwardingTargetForSelector:aSelector];
    }
}

forwardInvocation

如果上面两个方法都无法处理这个selector,则会触发消息转发机制

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation

methodSignatureForSelector尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。如果能获取,则返回非nil:创建一个 NSlnvocation 并传给forwardInvocation:。
NSMethodSignature用来表示方法的签名信息:返回值,参数数量和类型。
NSInvocation包含完整的消息,SEL + 执行SEL的Target + 参数值
forwardInvocation我们可以做以下事情:

如果forwardInvocation还不能处理这个selector,就会直接调用doesNotRecognizeSelector抛出异常。

下面是改变选择子的例子,比如我们直接调用的是playPiano方法,最后转发给了traval:方法:

// 完整的消息转发
- (void)travel:(NSString*)city
{
    NSLog(@"Teacher travel:%@", city);
}

- (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
{
    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];
}
上一篇下一篇

猜你喜欢

热点阅读