iOS的消息转发机制
iOS的消息转发机制是在消息发送给对象时,找不到对应的实例方法的情况下启动的。消息转发允许对象在运行时处理无法识别的消息,提供了一种动态的、灵活的消息处理方式。
消息转发机制主要分为三个阶段:
- 动态方法解析
- 快速转发
- 标准转发
1. 动态方法解析
在这个阶段,运行时系统会询问对象是否能动态添加方法来处理未知消息。可以通过实现 +resolveInstanceMethod:
或 +resolveClassMethod:
方法来动态添加方法实现。
objc
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(someMethod)) {
class_addMethod([self class], sel, (IMP)someMethodImplementation, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void someMethodImplementation(id self, SEL _cmd) {
NSLog(@"Dynamically added method");
}
2. 快速转发
如果动态方法解析失败,运行时会调用 -forwardingTargetForSelector:
方法,询问对象是否有其他对象可以处理该消息。通过返回另一个对象的实例,消息可以转发给该实例。
objc
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(someMethod)) {
return alternateObject;
}
return [super forwardingTargetForSelector:aSelector];
}
3. 标准转发
如果前两个阶段都失败,运行时会调用 -methodSignatureForSelector:
和 -forwardInvocation:
方法进行标准消息转发。首先,通过 -methodSignatureForSelector:
方法获取方法签名,然后在 -forwardInvocation:
方法中处理消息。
objc
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(someMethod)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([alternateObject respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:alternateObject];
} else {
[super forwardInvocation:anInvocation];
}
}
图示
为了更好地理解这个过程,下面是一个示意图:
plaintext
+----------------------+
| Sender |
+----------------------+
|
| Message: someMethod
v
+----------------------+
| Receiver |
+----------------------+
| |
| -resolveInstanceMethod:
| - Can add method? ----> YES (add method)
| |
| v
| NO |
| |
| -forwardingTargetForSelector:
| - Other object to handle? ----> YES (forward to other object)
| |
| v
| NO |
| |
| -methodSignatureForSelector:
| - Get method signature ----> Signature or nil
| |
| v
| Signature |
| |
| -forwardInvocation:
| - Handle invocation ----> Forward or error
+----------------------+
示例代码
下面是一个完整的示例代码,展示如何使用消息转发机制:
objc
#import <objc/runtime.h>
@interface SQIAlternateObject : NSObject
- (void)someMethod;
@end
@implementation SQIAlternateObject
- (void)someMethod {
NSLog(@"AlternateObject handling someMethod");
}
@end
@interface MyObject : NSObject
@end
@implementation MyObject {
SQIAlternateObject *alternateObject;
}
- (instancetype)init {
self = [super init];
if (self) {
alternateObject = [[SQIAlternateObject alloc] init];
}
return self;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(someMethod)) {
class_addMethod([self class], sel, (IMP)someMethodImplementation, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void someMethodImplementation(id self, SEL _cmd) {
NSLog(@"Dynamically added method");
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(someMethod)) {
return alternateObject;
}
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(someMethod)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([alternateObject respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:alternateObject];
} else {
[super forwardInvocation:anInvocation];
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MyObject *obj = [[MyObject alloc] init];
[obj someMethod];
}
return 0;
}
在这个示例中,我们展示了如何通过动态方法解析、快速转发和标准转发来处理 someMethod
方法。