最近我在java(for android)中编写了一个应用程序,它使用反射来调用某些对象的方法。参数号和类型是未知的,这意味着,我有一个统一的机制,它接收一个对象名,方法名和参数数组(使用JSON),并使用参数数组(Object [])调用指定对象上的指定方法充满了所需类型的参数。)
现在我需要为iOS实现相同的功能,当我知道选择器所需的参数数量时,我能够调用一个选择器:
SEL selector = NSSelectorFromString(@"FooWithOneArg");
[view performSelectorInBackground:selector withObject:someArg];
我知道我可以使用
获取选择器接收的参数数量int numberOfArguments = method_getNumberOfArguments(selector);
但有没有办法像这样进行通用调用:
[someObject performSelector:selector withObject:arrayOfObjects]
几乎相当于Java的
someMethod.invoke(someObject, argumentsArray[]);
我想根据选择器获取的参数数量来避免切换案例。
很抱歉,我只想尽可能清楚地提出问题。
答案 0 :(得分:12)
这个小功能应该可以解决问题,但并不完美,但它为您提供了一个起点:
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
}
答案 1 :(得分:3)
这里有很棒的帮助,包括user102008的简单但完美的答案,我把以下的例子拉到一起。注意我真正想要做的是允许某人向我发送一个目标选择器,该选择器要么做了也没有做出参数。如果它需要一个参数我假设他们希望调用对象的“self”作为参考返回:
NSMethodSignature * sig = [target methodSignatureForSelector:selector];
if ([sig numberOfArguments] > 0) {
[target performSelector:selector withObject:self];
}
else {
[target performSelector:selector];
}
希望这有助于有人挖掘。
答案 2 :(得分:2)
我修改了@JustSid答案并添加了更多验证,nil参数支持,将其更改为Obj-C NSObject类别方法,并添加-performSelectorIfAvailable:
辅助方法以便于使用。请享受! :)
#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
}
答案 3 :(得分:0)
为什么不定义每个方法来获取一个参数:对象数组?大概你想要的是,用方法
-(void) doSomethingWithFoo:(id) foo andBar: (id) bar;
使用从数组中设置的参数调用它。好吧,而是:
-(void) doSomethingWithArrayOfFooAndBar: (NSArray*) fooAndBar;
那么你的整个调度机制就变成了:
[someObject performSelector:selector withObject:arrayOfObjects];