美文网首页
iOS运行时消息转发

iOS运行时消息转发

作者: MiniCoder | 来源:发表于2020-03-08 18:30 被阅读0次

iOS开发过程中,有一类的错误会经常遇到,就是找不到所调用的方法,当然这类问题比较好解决,给当前对象或其父类对象添加该方法即可,使得编译器在编译时能正确找到该方法;或者,还有另外的方法,由于Objective-C是一门动态语言,我们也可以在运行期再给类添加该方法,一样可以解决该问题,而这就涉及了类的消息转发机制。

消息转发到底是什么呢? 这里将分为三个部分进行逐一讲解:
1、动态方法解析
2、备用接收者
3、完整消息转发


截屏2020-03-0819.01.00.png

1.动态方法解析
首先,Objective-C运行时会调用+resolveInstanceMethod:或者+resolveClassMethod:,让你有机会提供一个函数实现。
如果你添加了函数并返回YES, 那运行时系统就会重新启动一次消息发送的过程。

+(BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"未实现的调用方法  = %@",NSStringFromSelector(sel));
    IMP class = class_getMethodImplementation(self, @selector(functionOne));
    class_addMethod(self, sel, class, "v@:");
    return true;
}
-(void)functionOne{
    NSLog(@"这是方法一");
}

我们可以在resolveInstanceMethod 中添加方法并返回YES,上例中我添加了functionOne并成功执行了,如果未添加方法实现,则返回false,切记不能在该方法中添加未实现的方法,否则会进入死循环。(实践中如果添加了方法,无论返回ture或者false都会执行添加的方法,不会进入消息转发,返回true或false似乎没什么影响,不过还是按照官方文档来做吧)。那么消息将进入到消息转发forwardingTargetForSelector中。
添加方法中应该看到了v@:,这是定义方法的,我会写一篇文章专门讲解这个。

2.备用接收者
如果我们在resolveInstanceMethod没有执行添加的方法,并且返回了false,那么消息将会转发到这里。

///消息转发,如果在这里转发只可转发给一个对象或一个方法
- (id)forwardingTargetForSelector:(SEL)aSelector{
    NSLog(@"消息转发");
//    return self.control;
    return  nil;
}

我们需要传递一个对象来接收这个方法信息。分为两种:
1.是传递方法的接收者,那么消息将传递给该对象并查找对应的方法,我们定义的方法名称和参数个数必须相同。返回值可以不同。
2.如果传为nil,那么消息将进入到完整消息转发中。

3.完整消息转发
如果在上一步还不能处理未知消息,则唯一能做的就是启用完整的消息转发机制了。
消息转发分为两部分

//注册方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
/// 消息转发,可转发给多个对象,或者多个方法同时执行
- (void)forwardInvocation:(NSInvocation *)anInvocation;

首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。
如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象

///注册方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    return [NSMethodSignature signatureWithObjCTypes:"#@:#"];
}

这里我定义的参数签名为"#@:#",接收一个NSString类型的参数,返回一个NSString类型的返回值。

/// 消息转发,可转发给多个对象,或者多个方法同时执行
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"%@",anInvocation);
    [anInvocation invokeWithTarget:self.control];
}

这里我们将消息转为控制器对象去接收处理。

  NSString * value =  [person performSelector:@selector(run:) withObject:@"这是参数"];
  NSLog(@"返回值 = %@",value);
-(NSString * )run:(NSString * )value{
    
    NSLog(@"转发给控制器调用 %@ %s",value,__FUNCTION__);
    return @"这是返回值";
}

这里可以看到结果

 RunTime-iOS[27980:1126582] 转发给控制器调用 这是参数 -[ViewController run:]
 RunTime-iOS[27980:1126582] 返回值 = 这是返回值

控制器方法可以接收方法参数,也可以返回参数给最初的调用者。

相关文章

网友评论

      本文标题:iOS运行时消息转发

      本文链接:https://www.haomeiwen.com/subject/gphjdhtx.html