Skip to content

1.Runtime 的消息机制,objc_msgSend 方法调用流程

OC中的方法调用,其实都是转换为objc_msgSend()函数的调用(不包括[super message])
objc_msgSend()的执行流程可以分为3大阶段:消息发送、动态方法解析、消息转发

2.消息发送

objc_msgSend 的动作比较清晰:首先在 Class 中的缓存查找 imp(没缓存则初始化缓存),如果没找到,则向父类的 Class 查找。如果一直查到到根类仍旧没有实现,则用 _objc_msgForward 函数指针代替 imp。最后,执行这个 imp。 _objc_msgForward 是用于消息转发的,当方法没有被寻找到的时候,就会触发消息转发流程。

3.消息转发

【1、接收消息:】当一个对象收到一个它不理解或无法处理的消息时,运行时系统首先会尝试进行动态方法解析。

【2.动态方法解析:】在这个阶段,对象可以通过实现特定的方法来尝试为未知的消息提供实现。例如,+resolveInstanceMethod: 和 +resolveClassMethod: 方法允许为实例方法和类方法动态添加实现。如果这些方法无法解析消息,运行时系统会进入下一步。

【3.转发决策:】接下来,对象可以通过 -forwardingTargetForSelector: 方法来决定将消息转发给谁。这个方法返回一个对象,该对象将接收转发过来的消息。如果这个方法返回 nil,运行时系统将尝试获取消息的签名信息。

【4.获取签名信息:】-methodSignatureForSelector: 方法用于获取方法的签名信息,这样就知道如何处理消息的类型和参数。如果没有获取到签名信息,运行时系统将抛出异常。

【5.实际转发:】最后,如果上述步骤都未处理消息,则运行时系统将调用 -forwardInvocation: 方法,在该方法中实现消息的自定义处理逻辑。此时开发者可以决定如何处理这个未识别的消息。

在Objective-C中,Runtime(运行时)机制是一个强大的特性,使得程序在运行时能够动态地执行一些操作。消息机制是Runtime的核心之一,它使得Objective-C具备了动态调用方法的能力。下面是对Runtime消息机制的详细解析:

消息传递的基本流程

  1. 消息发送:在Objective-C中,当你调用一个方法时,实际上是在发送一个消息。例如 [object method],在底层被转换为 objc_msgSend(object, @selector(method))
  2. 消息接收:消息发送后,Runtime会根据接收对象和方法选择器(selector)来查找相应的方法实现。

消息传递的具体步骤

  1. 查找方法缓存:每个类都有一个方法缓存(method cache),它是一个哈希表,用于存储最近使用的方法实现。首先,Runtime会在缓存中查找是否存在对应的方法实现。如果找到,则直接调用。
  2. 查找类的方法列表:如果缓存中没有找到,Runtime会在类的方法列表中查找。每个类都有一个方法列表,存储了该类的所有方法实现。
  3. 查找父类的方法列表:如果在类的方法列表中仍然没有找到,Runtime会沿着继承链向上查找父类的方法列表,直到根类(通常是 NSObject)。
  4. 消息转发机制:如果在整个继承链中都没有找到对应的方法实现,Runtime会进入消息转发机制。

消息转发机制

  1. 动态方法解析:首先,Runtime会调用 +resolveInstanceMethod:+resolveClassMethod: 让你有机会动态添加方法实现。如果你在这个方法中动态添加了方法实现,消息发送会重新开始。

  2. 备用接收者:如果动态方法解析失败,Runtime会调用 -forwardingTargetForSelector: 方法,允许你指定一个备用对象来处理这个消息。如果指定了备用对象,消息会被转发到该对象。

  3. 完整的消息转发:如果备用接收者也不能处理该消息,Runtime会调用 -methodSignatureForSelector: 获取方法签名,然后调用 -forwardInvocation: 进行最终的消息转发。在 -forwardInvocation: 方法中,你可以将消息转发给另一个对象或者抛出异常。

示例代码

以下是一个关于消息转发机制的简单示例:

objective-c
#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface MyClass : NSObject
- (void)dynamicMethod;
@end

@implementation MyClass

void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"Dynamic method implementation");
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(dynamicMethod)) {
        class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    // Return an alternative object to handle the message
    return nil;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(dynamicMethod)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if (anInvocation.selector == @selector(dynamicMethod)) {
        NSLog(@"Forwarding invocation");
        // You can forward the invocation to another object here
    } else {
        [super forwardInvocation:anInvocation];
    }
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyClass *obj = [[MyClass alloc] init];
        [obj dynamicMethod];
    }
    return 0;
}

在这个示例中,我们通过 +resolveInstanceMethod: 动态添加了 dynamicMethod 的实现。如果没有添加实现,-forwardingTargetForSelector:-forwardInvocation: 方法将处理消息转发。

总结

Runtime的消息机制使得Objective-C具备了极高的动态性和灵活性。通过消息传递和转发机制,程序可以在运行时动态地决定如何响应消息,从而实现诸如动态方法添加、消息转发等高级特性。这也是Objective-C与其他静态语言相比的一个重要优势。

4.Runtime的主要应用

  • 反射机制
  • 关联对象
  • 方法交换
  • Hook方法

世界很美 而你正好有空