aryaxt
aryaxt

Reputation: 77596

Objective C - Get argument types of a method?

At runtime I need to be able to get the argument types of a method. The following is what gets printed:

I have read on other threads that at run-time time objective c treats all objects passed to a method as arguments as id. If this approach doesn't work any other suggestions on a way to read argument types?

Log

2014-02-07 15:47:08.962 OCInjection[55727:70b] @
2014-02-07 15:47:08.964 OCInjection[55727:70b] :

Code

Class class = NSClassFromString(injectionBinding);

unsigned int methodCount;
Method *methodList = class_copyMethodList(class, &methodCount);

for (int i = 0; i < methodCount; i++)
{
   Method method = methodList[i];
   SEL selector = method_getName(method);
   NSMethodSignature *signature = [class instanceMethodSignatureForSelector:selector];

   NSUInteger numberOfArguments = [signature numberOfArguments];

   for (int i=0 ; i<numberOfArguments ; i++)
   {
      NSString *type = [NSString stringWithUTF8String:[signature getArgumentTypeAtIndex:i]];
                    NSLog(type);
   }
}

Upvotes: 3

Views: 3283

Answers (2)

aryaxt
aryaxt

Reputation: 77596

Doesn't seem like it's possible to do this. I ended up using a proxy object to send the message to, and capture it. Probably not the ideal way, but I haven't found a better solution.

@interface DIContructorInjectorProxy()
@property (nonatomic, strong) id realObject;
@end

@implementation DIContructorInjectorProxy

#define Inject(x) [DIContructorInjectorProxy _injectMacro:x]

- (id)initWithClass:(Class)class
{
   self.realObject = [[class alloc] init];
}

+ (id)_injectMacro:(id)x
{
    if ([x isKindOfClass:NSClassFromString(@"Protocol")])
        return NSStringFromProtocol(x);
    else
        return NSStringFromClass(x);
}

- (id)withConstructor
{
    // Just making the method call for defining a constructor more readable by a call to this method first
    return self;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSMutableString *selectorName = [NSStringFromSelector(anInvocation.selector) mutableCopy];
    NSUInteger numberOfColonsInMethodName = [selectorName replaceOccurrencesOfString:@":"
                                                                           withString:@":"
                                                                              options:NSLiteralSearch
                                                                                range:NSMakeRange(0, selectorName.length)];

    [anInvocation retainArguments];
    NSMutableArray *argumentsPassedToSelector = [NSMutableArray array];

    for (int i=2 ; i<numberOfColonsInMethodName+2 ; i++)
    {
        NSString *argument;
        [anInvocation getArgument:&argument atIndex:i];
        [argumentsPassedToSelector addObject:[NSString stringWithFormat:@"%@", argument]];
    }

    // Store arguments somewhere

    return;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    return [self.realObject methodSignatureForSelector:aSelector];
}

@end

How the user uses this to define method arguments

[self bindProtocol:@protocol(DataStorage) toClass:[InMemoryDataStorage class]];

// withConstructor returns an appropriate proxy object
// Then when the init method is called, it calls forwardInvocation, 
// and from there I save all the info I need about the method and arguments
(void)[[[self bindProtocol:@protocol(GoogleClient) toClass:[GoogleClientEngine class]] withConstructor]
                initWithDataStorage:Inject(@protocol(DataStorage))];

Upvotes: 0

Chen-Hai Teng
Chen-Hai Teng

Reputation: 753

According to -getArgumentTypeAtIndex: and Decode Class from @encoded type string

I think there is no method to get the "real" argument type.

Upvotes: 1

Related Questions