使用reflection/自省调用具有未知数量参数的select器

最近我写了一个应用程序在Java(对于Android),它使用reflection来调用一些对象的方法。 参数号和types是未知的,这意味着,我有一个统一的机制,收到一个对象名称,方法名称和参数数组(使用JSON),并在指定的对象上调用指定的方法与参数(Object []充满了所需types的参数)。

现在我需要实现相同的iOS,我可以调用一个select器,当我知道select器预期的参数数量是这样的:

SEL selector = NSSelectorFromString(@"FooWithOneArg"); [view performSelectorInBackground:selector withObject:someArg]; 

我知道我可以通过使用select器接收的参数数量

 int numberOfArguments = method_getNumberOfArguments(selector); 

但有没有办法做一个这样的通用调用:

 [someObject performSelector:selector withObject:arrayOfObjects] 

这几乎等同于Java的

 someMethod.invoke(someObject, argumentsArray[]); 

我想根据select器得到的参数数量来避免一个开关情况。

对不起,很长一段时间,我只是想尽可能清楚我的问题。

这个小函数应该做的伎俩,它不完美,但它给你一个起点:

 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 } 

有了这里的真棒帮助,包括从user102008简单但完美的答案我拉到一起下面的例子。 注意我真正想要做的是让别人给我发送一个目标select器,这个目标select器可以做或不做任何争论。 如果它需要一个参数,我假设他们想调用对象的“自我”作为参考返回:

  NSMethodSignature * sig = [target methodSignatureForSelector:selector]; if ([sig numberOfArguments] > 0) { [target performSelector:selector withObject:self]; } else { [target performSelector:selector]; } 

希望这可以帮助人们挖掘。

我修改@JustSid的答案,并添加更多的validation,零参数支持,将其更改为Obj-C NSObject类别的方法,并添加-performSelectorIfAvailable: helper方法更容易使用。 请享受! 🙂

 #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 } 

为什么不定义每个方法来接受一个参数:对象数组? 大概你想要的是,与方法

 -(void) doSomethingWithFoo:(id) foo andBar: (id) bar; 

使用从数组中设置的参数调用它。 那么,而不是有:

 -(void) doSomethingWithArrayOfFooAndBar: (NSArray*) fooAndBar; 

那么你的整个调度机制就变成:

 [someObject performSelector:selector withObject:arrayOfObjects]; 
Interesting Posts