Runtime消息分发(三)

2018-05-06  本文已影响0人  wemt

上一节在描述Method数据结构时,区分了SEL和IMP。知道了在OC中是通过发送消息来执行代码的。
消息发送的流程也只有两步:

  1. 通过SEL查找IMP。
  2. 执行IMP。

那么SEL是怎样去查找IMP的呢?如果找不到会怎么办?
1.1 首先会在本类找,如果找不到则会在其继承链上寻找。
1.2 在动态绑定的方法中寻找IMP。
1.3 消息转发。

由于前面两步都没有提供回调,所以只需要将注意力放在第三步消息转发上面。
消息转发又分成3个步骤:
1.3.1 动态方法解析
1.3.2 备援接收者
1.3.3 完整消息转发

一个完整SEL查找IMP的过程如图:


SEL寻找IMP过程.png

动态方法解析

在没有找到IMP时,首先会调用下面的方法:

//类方法
+ (BOOL)resolveClassMethod:(SEL)sel
//实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel

在这个方法中,只需要给对象动态添加一个方法并且返回YES,表明可以处理。

//自定义的C函数。
void messageBirthDate(){
    NSLog(@"CType :MessageForward birthDate");
}
//@param sel  方法的选择子
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if ([@"messageBirthDate" isEqualToString:NSStringFromSelector(selector)]){
        //添加方法
        //@param class 对象的类
        //@param sel 选择子
        //@param IMP IMP
        //@param types 参数和返回类型编码
        class_addMethod([self class], selector, (IMP)messageBirthDate, "vv");
        return YES;
    }
    return [super resolveInstanceMethod:selector];
}

备援接受者

如果动态方法解析没有进行处理,则会调用下面的方法,看能否进行处理:

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(birthDate))
    {
        return [SonClass new];
    }
    return [super forwardingTargetForSelector:aSelector];
}

这个方法实际上是将消息转发到有对应SEL的类去进行处理。

完整的消息转发

如果备援接受者依然没有进行处理,则会进行完整的消息转发。完整的消息转发其实也就只有2步:
1.获取方法签名
2.执行函数调用相关内容。

//////////////消息转发
//1.获取方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    if (!signature) {
        FatherClass *father = [[FatherClass alloc] init];
        signature = [father methodSignatureForSelector:@selector(birthDate)];//将fatherBirthDate的Signature转成 → birthDate的Signature
    }
    return signature;
}
//2.执行函数调用相关
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
//自己新组成一个invokation。
//    NSMethodSignature *signature= [anInvocation methodSignature];//可以从前面anInvocation中去取。
    NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"@@:"];//也可以自己去生成。
    NSInvocation *newInvocation =[NSInvocation invocationWithMethodSignature:signature];
    FatherClass *father =[[FatherClass alloc] init]; 
    [newInvocation setTarget:father];
    [newInvocation setSelector:@selector(birthDate)];
    [newInvocation invoke];
    id retValue;
    [newInvocation getReturnValue:&retValue];
    NSLog(@"%@",(NSString *)retValue);
    [anInvocation setReturnValue:&retValue];
}

这里需要注意变量的作用域,在setTarget时,newInvocation不会去持有Target。所以需要自己控制变量的作用域。
在消息转发过程中,越靠后消息处理的成本也越大,但是能够进行的掌控也越多。至于需要在哪个部分进行截获处理,就需要按照具体需求来判断了。

异常处理

当完整详细转发还不能够处理时,则会走到异常处理了:

- (void)doesNotRecognizeSelector:(SEL)aSelector
{
    [super doesNotRecognizeSelector:aSelector];
}

在异常处理中,可以做一些异常上报的工作。

总结

1.介绍了SEL寻找IMP的过程。
2.介绍了消息转发的过程。


参考:

上一篇 下一篇

猜你喜欢

热点阅读