目标C语言 使用反射/自省调用参数数量未知的选择器



最近我用java (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[]);

?

我想根据选择器获得的参数数量来避免切换情况。

我只是想把我的问题尽可能地说清楚。

这个小函数应该可以做到这一点,它并不完美,但它给了您一个起点:

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的简单但完美的答案,我把下面的例子放在一起。注意我真正尝试做的是允许别人给我发送一个目标选择器它要么带参数要么不带参数。如果它接受一个参数,我假设他们希望调用对象的"self"作为引用返回:

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

我修改了@JustSid答案并添加了更多验证,nil参数支持,将其更改为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];

相关内容

  • 没有找到相关文章

最新更新