Question

I have an array of methods I have to fire in sequence. Every method returns a BOOL. Something like

- (BOOL) oneMethod;

The method names are on an array like

#define TESTS  @[         \
    @"averageNotOK:",     \
    @"numbersOverRange:", \
    @"numbersUnderRange:",\
    @"numbersForbidden:", \
    // ... etc etc
    @"numbersNotOnCurve:"]

The methods run in a loop. Something like

- (BOOL) numbersPassedAllTests:(NSArray *)numbers {

  NSInteger count = [TESTS count];

  for (int i=0; i<count; i++) {

        NSString *aMethodName = TESTS[i];

        SEL selector = NSSelectorFromString(aMethodName);

        BOOL failed = NO;

        NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];

        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setSelector:selector];
        [invocation setTarget:self];
        [invocation setArgument:&numbers atIndex:2];
        [invocation invoke];


        [invocation getReturnValue:&failed];

        if (failed) {
          return NO;
        }
  }
  return YES;

}

Is there a way to run these methods without using NSInvocation?

Yes, I need the methods on an array of strings (not array of selectors), because I need their names on other contexts. I know that I cannot use performSelector because it returns void and I need something to return BOOL. Yes, I know about an extension for performSelector that returns anything, but it uses NSInvocation and I am trying to discover another method to do this without using NSInvocation.

Any ideas?

Was it helpful?

Solution

I found the best answer here

The answer is to replace the whole thing with this:

#import <objc/message.h>
- (BOOL) numbersPassedAllTests:(NSArray *)numbers {

  NSInteger count = [TESTS count];

  for (int i=0; i<count; i++) {

        NSString *aMethodName = TESTS[i];

        SEL selector = NSSelectorFromString(aMethodName);

        BOOL (*BOOLMsgSend)(id, SEL, id) = (BOOL (*)(id, SEL, id)) objc_msgSend;
        BOOL failed = BOOLMsgSend(self, selector, numbers);

        if (failed) {
          return NO;
        }
  }
  return YES;

}

simply, beautiful and at least 30% faster in my tests.

OTHER TIPS

Your other option is:

[self performSelector:selector withObject:numbers];

You would need to store the result somewhere, like add a property to the current object that is:

@property (nonatomic, assign) BOOL lastTestResult;

Have each test set that and then check it after performSelector.

But I doubt that is really going to be any different. It looks like you're doing some sort of academic mathy thing. If the performance of invoking these calls really matters, consider just making them C functions and using an array of function pointers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top