Calling a selector with unknown number of arguments using reflection / introspection

后端 未结 4 1584
-上瘾入骨i
-上瘾入骨i 2020-12-16 07:39

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

相关标签:
4条回答
  • 2020-12-16 07:41

    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
    }
    
    0 讨论(0)
  • 2020-12-16 07:47

    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
    }
    
    0 讨论(0)
  • 2020-12-16 07:56

    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.

    0 讨论(0)
  • 2020-12-16 07:59

    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];
    
    0 讨论(0)
提交回复
热议问题