我正在使用Objective-C的forwardInvocation:功能,我需要知道该方法接收的参数类型。在我的示例中,我为它传递了一个int,但是getArgumentTypeAtIndex:告诉我它是一个id。这是一个简单的示例:

@interface Do : NSObject
+ (void) stuff:(int)x;
@end
@implementation Do
+ (NSMethodSignature *) methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature)
        signature = [self methodSignatureForSelector:@selector(forwardInvocation:)];
    return signature;
}

+ (void)forwardInvocation:(NSInvocation *)i
{
    const char* argType = [i.methodSignature getArgumentTypeAtIndex:2];
    NSLog(@"%s == %s", argType, @encode(id)); // @ == @
    NSLog(@"%s == %s", argType, @encode(int)); // @ == i
}
@end


这是我的称呼:

[Do stuff:123];


知道为什么我没有得到id而不是int作为类型吗?

最佳答案

问题是您实际上在类上没有stuff:方法,因此methodSignatureForSelector:将返回nil-似乎您发现了该内容并因此实现了自己的版本,但是在super上失败了呼叫,因此最终返回forwardInvocation:的签名-这不是您想要的!

为了解决这个问题,您要么需要将methodSignatureForSelector:定向到具有选择器的类,要么使用协议-如果一个类实现了协议,那么它将为该协议中的任何方法返回签名,即使这些方法没有实际上是由该类实现的。

这是使用协议的示例:

@protocol DoProtocol
@optional
+ (void) stuff:(int)x;
@end

@interface Do : NSObject<DoProtocol>
@end

@implementation Do

+ (void)forwardInvocation:(NSInvocation *)i
{
   const char* argType = [i.methodSignature getArgumentTypeAtIndex:2];
   NSLog(@"%s == %s", argType, @encode(id)); // @ == @
   NSLog(@"%s == %s", argType, @encode(int)); // @ == i
}

@end


@optional避免了未实现方法的任何编译器警告。 methodSignatureForSelector:(来自NSObject)的默认实现将返回从协议获得的有效签名,因此将调用forwardInvocation:

10-07 20:02