iOS 消息转发机制

720 阅读4分钟

iOS消息转发机制是Objective-C运行时系统提供的一种灵活机制,允许对象在运行时处理无法直接响应的消息。当一个对象收到一个它无法直接识别的方法调用时,这个消息并不会立即导致程序崩溃,而是会通过一系列步骤尝试找到能够处理该消息的对象或方法。 在iOS中有三次机会可以进行方法转发, 以下是iOS消息转发机制的详细解释:

@interface MyClass : NSObject
@end

@implementation MyClass

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

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(someMethod)) {
        return [[AnotherClass alloc] init];
    }
    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 ([anotherObject respondsToSelector:[anInvocation selector]]) {
        [anInvocation invokeWithTarget:anotherObject];
    } else {
        [super forwardInvocation:anInvocation];
    }
}

// 动态方法实现的函数
void someMethodImplementation(id self, SEL _cmd) {
    NSLog(@"Dynamically added method");
}

@end
开始
    |
    V
检查对象是否能直接响应消息
    | 是 -> 执行消息对应的方法(someMethod),结束
    | 否 ->
    V
尝试动态方法解析(resolveInstanceMethod)
    | 是 -> 动态添加方法并执行,结束
    | 否 ->
    V
尝试消息重定向(forwardingTargetForSelector)
    | 找到备援接收者 -> 转发消息给备援接收者,结束
    | 否 ->
    V
进入完整消息转发机制
    | 创建NSInvocation对象封装消息
    | 调用-methodSignatureForSelector:获取方法签名
    | 调用-forwardInvocation:处理消息转发
        | 转发消息给目标对象并执行
        | 或执行其他自定义处理逻辑
    |
    V
如果没有找到能处理消息的对象
    | 抛出"unrecognized selector sent to instance"异常

结束

工作流程

当一个对象收到一个它无法直接响应的消息时,消息转发机制会按照以下步骤进行:

  1. 动态方法解析

    • Objective-C运行时首先会尝试通过+resolveInstanceMethod:(对于实例方法)或+resolveClassMethod:(对于类方法)方法动态地向类中添加一个新方法。
    • 如果这些方法返回YES,并且成功地将一个新方法添加到了类中,那么原始的消息就会被这个新添加的方法处理,消息转发流程就此结束。
    • 这一步允许对象在运行时动态地添加或修改方法实现。
  2. 备援接收者

    • 如果动态方法解析失败,Objective-C运行时接下来会调用-forwardingTargetForSelector:方法。
    • 在这个方法中,对象可以返回一个能够响应该消息的其他对象。
    • 如果这个方法返回了一个非nil的对象,那么原始的消息就会被转发到这个备援接收者上,消息转发流程就此结束。
    • 这一步提供了一种机制,使得对象可以将无法处理的消息转发给其他对象来处理。
  3. 完整消息转发

    • 如果前两步都失败了,那么消息转发流程会进入完整消息转发阶段。
    • 首先,Objective-C运行时会调用-methodSignatureForSelector:方法来获取一个NSMethodSignature对象,这个对象描述了原始消息的参数类型和返回类型。
    • 接着,运行时会调用-forwardInvocation:方法,并将一个封装了原始消息调用的NSInvocation对象作为参数传入。
    • -forwardInvocation:方法中,对象可以自定义处理这个NSInvocation对象。例如,它可以将消息转发给另一个对象,或者执行一些其他操作来处理这个消息。
    • 如果-forwardInvocation:方法被调用并且成功处理了消息,那么消息转发流程就此结束。
    • 如果-forwardInvocation:方法没有实现或者无法处理消息,那么Objective-C运行时最终会抛出一个unrecognized selector sent to instance异常,导致程序崩溃。

实际应用

消息转发机制在iOS开发中有着广泛的应用场景。例如,它可以用于实现代理模式、观察者模式、回调机制等设计模式。此外,它还可以用于处理那些无法通过正常类继承和方法实现来解决的情况,比如动态地添加或修改方法实现、将消息转发给其他对象等。

注意事项

  • 在使用消息转发机制时,需要确保不会陷入无限循环。例如,在-forwardingTargetForSelector:方法中返回的对象不应该再次将消息转发回原始对象。
  • 消息转发机制虽然提供了很大的灵活性,但也会增加程序的复杂性。因此,在使用时需要谨慎,并确保不会滥用这一机制。
  • 开发者应该尽量通过正常的类继承和方法实现来处理消息,而不是依赖消息转发机制。消息转发机制应该被视为一种备用方案,用于处理那些无法通过正常手段解决的情况。