ObjC Runtime简析-- objc_MsgSend

1,918 阅读3分钟

在ObjC中,方法的调用是通过消息机制依赖runtime来实现的。使用[]给对象发送一个消息,转化为C++的实现是调用了objc_msgSend()函数。

objc_msgSend()函数在runtime源码中是通过汇编代码实现的。它存在与runtime源码的这个位置:

objc_msgSend()的源码

通过跟读源码我们发现整个objc_msgSend的调用流程是这样的:

通过上图我们发现objc_msgSend的调用大致分为三个流程:

  • 消息发送
  • 动态方法解析
  • 消息转发

消息发送

通过上图我们可以看出,消息发送经过了判定消息接受者是否为nil,然后从缓存中查找方法,如果依然查找不到会递归getMethodNoSuper_nolock查找父类的方法缓存列表和父类的方法列表。 如果整个过程下来都找不到需要调用的方法,就会进如动态方法解析阶段。

动态方法解析

当进行方法的调用的时候如果找不到方法,会按照调用的方法类型去调用相应的动态解析方法,如果调用的是实例方法则会尝试调用+ (BOOL)resolveInstanceMethod:(SEL)sel方法,如果调用的是类方法,则会尝试调用+ (BOOL)resolveClassMethod:(SEL)sel方法。我们可以在动态解析中尝试为该方法添加一个新的已经实现了的方法,如下所示:

// 类方法
+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(test)) {
        Method method = class_getClassMethod(self, @selector(test2));
        class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

+ (void)test2 {
    NSLog(@"%s",__func__);
}

// 实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
        Method method = class_getInstanceMethod(self, @selector(test2));
        class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

- (void)test2 {
    NSLog(@"%s",__func__);
}

消息转发

如果消息机制进行到动态方法解析的时候依然找不到需要调用的方法,那么就会进入消息转发阶段。 消息转发阶段会视调用的方法的类型调用转发方法,实例方法调用- (id)forwardingTargetForSelector:(SEL)aSelector,类方法则调用+ (id)forwardingTargetForSelector:(SEL)aSelector方法。该方法要求返回一个可以接受这个消息的对象。 比如Person调用test方法,而Person没有实现test方法,而Student类实现了这个方法。那么就可以将这个消息转发给Student去处理:

// 实例方法
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test)) {
        return [[Student alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

// 类方法
+ (id)forwardingTargetForSelector:(SEL)aSelector
{
        if (aSelector == @selector(test)) {
            return [[Student alloc] init];
        }
    return [super forwardingTargetForSelector:aSelector];
}

返回的Student对象,实际上就相当于调用了objc_msgSend([Student new], aSelector)

如果上述方法没有实现或者说没有返回一个可以处理消息的对象,那么就会进入方法签名,然后forwardInvocation阶段。

这个阶段也会视消息类型调用不同的实例方法和类方法: 实例方法:- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector- (void)forwardInvocation:(NSInvocation *)anInvocation;类方法:+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector+ (void)forwardInvocation:(NSInvocation *)anInvocation

methodSignatureForSelector方法返回一个方法签名,方法签名就是按照编码规则生成的一个字符串的签名实例。编码规则在Runtime简析中已经介绍过,其实就是用method_t结构中的types初始化的实例,它代表了方法的结构,比如返回值类型,参数类型等。

经过methodSignatureForSelector签名之后就可以Invocation,提供一个可以调用该方法的实施作为target,返回。

// 实例方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
   if (aSelector == @selector(test)) {
       return [NSMethodSignature signatureWithObjCTypes:"v@:"];
   }
   return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
   [anInvocation invokeWithTarget:[[Student alloc] init]];
}

// 类方法
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
   if (aSelector == @selector(test)) {
       return [NSMethodSignature signatureWithObjCTypes:"v@:"];
   }
   return [super methodSignatureForSelector:aSelector];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
   [anInvocation invokeWithTarget:[[Student alloc] init]];
}

END

ObJC中的方法调用的本质是消息机制,通过objc_msgSend,经过三个阶段:方法查找,动态解析,和消息转发阶段,如果这几个阶段都没有提供解决方案,那么就会抛出经典的unrecognized selector sent to class错误。