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消息机制的详细解析:
消息传递的基本流程
- 消息发送:在Objective-C中,当你调用一个方法时,实际上是在发送一个消息。例如
[object method],在底层被转换为objc_msgSend(object, @selector(method))。 - 消息接收:消息发送后,Runtime会根据接收对象和方法选择器(selector)来查找相应的方法实现。
消息传递的具体步骤
- 查找方法缓存:每个类都有一个方法缓存(method cache),它是一个哈希表,用于存储最近使用的方法实现。首先,Runtime会在缓存中查找是否存在对应的方法实现。如果找到,则直接调用。
- 查找类的方法列表:如果缓存中没有找到,Runtime会在类的方法列表中查找。每个类都有一个方法列表,存储了该类的所有方法实现。
- 查找父类的方法列表:如果在类的方法列表中仍然没有找到,Runtime会沿着继承链向上查找父类的方法列表,直到根类(通常是
NSObject)。 - 消息转发机制:如果在整个继承链中都没有找到对应的方法实现,Runtime会进入消息转发机制。
消息转发机制
动态方法解析:首先,Runtime会调用
+resolveInstanceMethod:或+resolveClassMethod:让你有机会动态添加方法实现。如果你在这个方法中动态添加了方法实现,消息发送会重新开始。备用接收者:如果动态方法解析失败,Runtime会调用
-forwardingTargetForSelector:方法,允许你指定一个备用对象来处理这个消息。如果指定了备用对象,消息会被转发到该对象。完整的消息转发:如果备用接收者也不能处理该消息,Runtime会调用
-methodSignatureForSelector:获取方法签名,然后调用-forwardInvocation:进行最终的消息转发。在-forwardInvocation:方法中,你可以将消息转发给另一个对象或者抛出异常。
示例代码
以下是一个关于消息转发机制的简单示例:
#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方法
