Lately I wrote an application in java (for android) which used reflection to invoke methods of some objects. The argument number and type was unknown, meaning, I had a unifi
This small function should do the trick, its not perfect, but it gives you a starting point:
void invokeSelector(id object, SEL selector, NSArray *arguments)
{
Method method = class_getInstanceMethod([object class], selector);
int argumentCount = method_getNumberOfArguments(method);
if(argumentCount > [arguments count])
return; // Not enough arguments in the array
NSMethodSignature *signature = [object methodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:object];
[invocation setSelector:selector];
for(int i=0; i<[arguments count]; i++)
{
id arg = [arguments objectAtIndex:i];
[invocation setArgument:&arg atIndex:i+2]; // The first two arguments are the hidden arguments self and _cmd
}
[invocation invoke]; // Invoke the selector
}
I modified @JustSid answer and added more validation, nil argument support, changed it to an Obj-C NSObject category method, and add -performSelectorIfAvailable:
helper methods for easier use. Please enjoy! :)
#import <objc/runtime.h>
@implementation NSObject (performSelectorIfAvailable)
// Invokes a selector with an arbitrary number of arguments.
// Non responding selector or too few arguments will make this method do nothing.
// You can pass [NSNull null] objects for nil arguments.
- (void)invokeSelector:(SEL)selector arguments:(NSArray*)arguments {
if (![self respondsToSelector:selector]) return; // selector not found
// From -numberOfArguments doc,
// "There are always at least 2 arguments, because an NSMethodSignature object includes the hidden arguments self and _cmd, which are the first two arguments passed to every method implementation."
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
int numSelArgs = [signature numberOfArguments] - 2;
if (numSelArgs > [arguments count]) return; // not enough arguments in the array
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:selector];
for(int i=0; i < numSelArgs; i++) {
id arg = [arguments objectAtIndex:i];
if (![arg isKindOfClass:[NSNull class]]) {
[invocation setArgument:&arg atIndex:i + 2];
}
}
[invocation invoke]; // Invoke the selector
}
With the awesome help here including the simple but perfect answer from user102008 I pulled together the following example. Note what I was really trying to do was allow someone to send me a target selector that either did or did not take an argument. If it takes an argument I assume they want the calling object's "self" returned as a reference:
NSMethodSignature * sig = [target methodSignatureForSelector:selector];
if ([sig numberOfArguments] > 0) {
[target performSelector:selector withObject:self];
}
else {
[target performSelector:selector];
}
Hope this helps someone digging around.
Why not define each of your methods to take one argument: the array of objects? Presumably what you want is, with with the method
-(void) doSomethingWithFoo:(id) foo andBar: (id) bar;
to invoke it with the parameters set from the array. Well, instead have:
-(void) doSomethingWithArrayOfFooAndBar: (NSArray*) fooAndBar;
then your whole dispatch mechanism just becomes:
[someObject performSelector:selector withObject:arrayOfObjects];