返回

拒绝滥杀无辜!拦截unrecognized selector 拦截的正确方式

IOS

unrecognized selector 拦截是一种常见的需求,它可以在对象没有实现某个方法时拦截并处理该方法调用。unrecognized selector 拦截有两种主要方式:动态方法解析和消息转发。

动态方法解析

动态方法解析是在编译时完成的。当编译器遇到一个对象没有实现的方法调用时,它会自动生成一个动态方法解析函数。该函数会在运行时被调用,以确定如何处理该方法调用。

动态方法解析有以下优点:

  • 速度快,因为它是编译时完成的。
  • 可以在编译时检测到方法不存在的错误。
  • 可以通过重写动态方法解析函数来定制方法的处理方式。

动态方法解析也有以下缺点:

  • 只能拦截在编译时已知的类的方法调用。
  • 不能拦截在运行时动态创建的类的方法调用。

消息转发

消息转发是在运行时完成的。当对象收到一个它没有实现的方法调用时,它会将该方法调用转发给另一个对象。

消息转发有以下优点:

  • 可以拦截任何对象的方法调用,无论该类是否在编译时已知。
  • 可以拦截在运行时动态创建的类的方法调用。

消息转发也有以下缺点:

  • 速度慢,因为它是在运行时完成的。
  • 难以调试,因为方法调用的实际处理位置可能很难找到。

如何选择拦截方案

在选择 unrecognized selector 拦截方案时,需要考虑以下因素:

  • 需要拦截的方法调用的类型。
  • 拦截方案的速度要求。
  • 拦截方案的调试难度。

如果需要拦截在编译时已知的类的方法调用,并且对速度要求不高,那么可以使用动态方法解析。如果需要拦截任何对象的方法调用,或者对速度要求很高,那么可以使用消息转发。

建议

建议使用消息转发来拦截 unrecognized selector。消息转发虽然速度较慢,但它可以拦截任何对象的方法调用,并且调试起来相对容易。

以下是一些使用消息转发来拦截 unrecognized selector 的示例代码:

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if ([self respondsToSelector:aSelector]) {
        return self;
    } else {
        return nil;
    }
}

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

- (id)forwardInvocation:(NSInvocation *)anInvocation {
    SEL selector = [anInvocation selector];
    if ([self respondsToSelector:selector]) {
        [anInvocation invokeWithTarget:self];
    } else {
        [self doesNotRecognizeSelector:selector];
    }
}

这只是使用消息转发来拦截 unrecognized selector 的一种方法。还有许多其他方法可以实现这一目标。开发者可以根据自己的具体需求来选择最合适的方法。