ObjC Runtime简析-- objc_MsgSend

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

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

objc_msgSend()的源码

经过跟读源码咱们发现整个objc_msgSend的调用流程是这样的:函数

经过上图咱们发现objc_msgSend的调用大体分为三个流程:post

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

消息发送

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

动态方法解析

当进行方法的调用的时候若是找不到方法,会按照调用的方法类型去调用相应的动态解析方法,若是调用的是实例方法则会尝试调用+ (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去处理:spa

// 实例方法
- (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)code

若是上述方法没有实现或者说没有返回一个能够处理消息的对象,那么就会进入方法签名,而后forwardInvocation阶段。cdn

这个阶段也会视消息类型调用不一样的实例方法和类方法: 实例方法:- (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错误。

相关文章
相关标签/搜索