Runtime消息发送与转发

249 阅读2分钟

Runtime消息发送

OC代码

Person *obj = [Person new]; 
[obj eat];

编译后

objc_msgSend(obj, @selector(eat));

objc_msgSend发送消息流程

  1. 通过isa指针找到所属类
  2. 在所属类缓存列表里面查找,如果没有则下一步
  3. 所属类的方法列表查找,找到与选择器方法名称相符的方法,跳到实现代码
  4. 找不到, 就沿着继承体系继续向上查找
  5. 如果能找到与选择器名称相符的方法, 就跳至其实现代码
  6. 找不到,执行消息转发

Runtime消息转发

消息转发流程

  1. 动态解析
  2. 快速转发
  3. 慢速转发

1.消息动态解析

如果是实例方法没有实现,动态解析调用

+(BOOL)resolveInstanceMethod:(SEL)sel

如果是类方法没有实现,动态解析调用

+(BOOL)resolveClassMethod:(SEL)sel

实例方法为例,代码如下:

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    BOOL isEqualSEL =  sel_isEqual(sel, @selector(btnAction:));
    if (isEqualSEL == YES) {
        //把IMP指针加入到 receiver 方法列表
        class_addMethod([self class], @selector(btnAction:), (IMP)addMessage, "v@:");
        return YES;
    }

    return [super resolveInstanceMethod:sel];
}

//IMP实现
void addMessage(id self, SEL _cmd) {
    //do something
}

2.快速转发

快速转发,调用系统Api

-(id)forwardingTargetForSelector:(SEL)aSelector;

返回一个新的类对象,新的类对象实现了当前类对象没有实现的方法

- (id)forwardingTargetForSelector:(SEL)aSelector  {
    Custom *obj = [Custom new];
     if (sel_isEqual(aSelector, @selector(btnAction))) {
         return obj;
     }
     return nil;
}

@interface Custom : NSObject

@end
@implementation Custom

- (void)btnAction {
    // do something
}

3.慢速转发

慢速转发使用两个对象 NSInvocationNSMethodSignature

主要用到如下Api


- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;//在协议中实现,返回相关的方法签名
- (void)forwardInvocation:(NSInvocation *)anInvocation;//进行消息的委派
- (void)invokeWithTarget:(id)anObject;//唤醒接受者的同名方法
- (void)doesNotRecognizeSelector:(SEL)aSelector;/

相关代码如下:

//重定向的实现类
@interface Custom : NSObject

@end
@implementation Custom

- (void)btnAction {
    // do something
}

@end

//慢速转发实现
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (sel_isEqual(aSelector, @selector(btnAction))) {
       //创建新的方法签名
        NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
        return methodSignature;
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"do method : %@", NSStringFromSelector(_cmd));
    FarwardTarget *target = [[FarwardTarget alloc] init];
    if ([target respondsToSelector:anInvocation.selector]) {
        //唤醒接受者的同名方法
        [anInvocation invokeWithTarget:target];
    }else {
        [self doesNotRecognizeSelector:anInvocation.selector];
    }
}

总结

  • 动态解析 : 适用于将原来的类中的方法替换掉或者延迟加载。
  • 快速转发 : 可以将消息处理转发给其他对象,使用范围更广,不只是限于原来的对象。
  • 慢速转发 : 跟快转发一样可以消息转发,但它能通过NSInvocation对象获取更多消息发送的信息,例如:target、selector、arguments和返回值等信息。