Is it possible to pass a method as an argument in Objective-C?

14,814

Solution 1

NSInvocation is a class for wrapping up a method calls in an object. You can set a selector (method signature), set arguments by index. You can then set a target and call invoke to trigger the call, or leave the target unset and use invokeWithTarget: in a loop of some sort to call this on many objects.

I think it works a little like this:

NSInvocation *inv = [[NSInvocation alloc] init];
[inv setSelector:@selector(foo:bar:)];
[inv setArgument:123 atIndex:0];
[inv setArgument:456 atIndex:1];

for (MyClass *myObj in myObjects) {
  [inv invokeWithTarget:myObj];
}

Or if you dont want to pass invocation objects into this method you can use the SEL type to accept a selector (method signature).

-(void)fooWithMethod:(SEL)selector;

Then assign the selector to an invocation object in order to call it on objects.

Solution 2

Or if you're using the fooWithMethod:(SEL)selector approach, just do [myObject performSelector:selector] on it, if it has no other arguments.

See NSObject for details.

Solution 3

As said before you can pass the selector of the method you want to call. Using a selector there are different ways to actually call the method:

  1. using NSObjects performSelector:, performSelector:withObject: and performSelector:withObject:withObject: methods
  2. using a NSInvocation object
  3. or directly using objc_msgSend or objc_msgSend_stret
  4. using the IMP of that method which you can get using methodForSelector:

Which one to use really depends on the situation. If the performance is not critical I’d go ahead with 1 if you need to pass in 0, 1 or 2 objects. If the performSelector:... methods don’t match I’d go with 2 or 3. Since setting up an NSInvocation object requires a lot of boilerplate code I prefer 3, but I guess that’s a matter of personal choice, unless there are performance issues.

If the performance of those method calls does matter I’d use 3 or 4. 3 should be faster unless you can cache the IMPs. But depending on your code this may not be feasible or doesn’t really help. So here you must profile the code and see which one is better for you.

Share:
14,814

Related videos on Youtube

eriaac
Author by

eriaac

Updated on February 26, 2020

Comments

  • eriaac
    eriaac over 4 years

    I have a method that varies by a single method call inside, and I'd like to pass the method/signature of the method that it varies by as an argument... is this possible in Objective C or is that too much to hope for?

  • mredig
    mredig over 8 years
    I just tried this and was unable to get it to work without first setting a method signature like this: NSMethodSignature* sig = [self methodSignatureForSelector:@selector(foo:bar:)]; NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig]; From there it should work.
  • mredig
    mredig over 8 years
    Also, be sure to start argument index at 2: Indices 0 and 1 indicate the hidden arguments self and _cmd, respectively; you should set these values directly with the target and selector properties. Use indices 2 and greater for the arguments normally passed in a message. - source