28

I have been using @selector today for the first time and have not been able to work out how to do the following? How would you write the @selector if you had more than one argument?

No arguments:

-(void)printText {
    NSLog(@"Fish");
}

[self performSelector:@selector(printText) withObject:nil afterDelay:0.25];

Single argument:

-(void)printText:(NSString *)myText {
    NSLog(@"Text = %@", myText);
}

[self performSelector:@selector(printText:) withObject:@"Cake" afterDelay:0.25];

Two arguments:

-(void)printText:(NSString *)myText andMore:(NSString *)extraText {
    NSLog(@"Text = %@ and %@", myText, extraText);
}

[self performSelector:@selector(printText:andMore:) withObject:@"Cake" withObject:@"Chips"];

Multiple Arguments: (i.e. more than 2)

NSInvocation

Alexandre
  • 13
  • 1
  • 4
fuzzygoat
  • 26,573
  • 48
  • 165
  • 294

9 Answers9

41

 

 - (id)performSelector:(SEL)aSelector
           withObject:(id)anObject  
           withObject:(id)anotherObject

From the Documentation:

This method is the same as performSelector: except that you can supply two arguments for aSelector. aSelector should identify a method that can take two arguments of type id. For methods with other argument types and return values, use NSInvocation.

so in your case you would use:

[self performSelector:@selector(printText:andMore:)
           withObject:@"Cake"
           withObject:@"More Cake"]
Alexandre
  • 13
  • 1
  • 4
cobbal
  • 69,903
  • 20
  • 143
  • 156
19

As an alternative for NSInvocation when you have more than two parameters, you can use NSObject's -methodForSelector: as in the following example:

SEL a_selector = ...
Type1 obj1 = ...
Type2 obj2 = ...
Type3 obj3 = ...
typedef void (*MethodType)(id, SEL, Type1, Type2, Type3);
MethodType methodToCall;
methodToCall = (MethodType)[target methodForSelector:a_selector];
methodToCall(target, a_selector, obj1, obj_of_type2, obj_of_type3);
Yoav
  • 5,962
  • 5
  • 39
  • 61
  • 1
    This is exactly what I was looking for. I needed to be able to pass in the parameters by reference, and couldn't figure out how to do it with performSelector. I did figure it out with NSInvocation, but I kept getting malloc errors. This did exactly what I needed it to do. – Nick Cipollina Jan 09 '13 at 15:30
14

I had an issue where I needed to use the afterDelay along with multiple arguments to my @selector method. Solution? Use a wrapper function!

Say this is the function I want to pass to @selector:

-(void)myFunct:(NSString *)arg1 andArg:(NSString *)arg2 andYetAnotherArg:(NSString *)arg3;

Obviously, I can't even use withObject: withObject: here, so, make a wrapper!

-(void)myFunctWrapper:(NSArray *)myArgs {
    [self myFunct:[myArgs objectAtIndex:0] andArg:[myArgs objectAtIndex:1] andYetAnotherArg:[myArgs objectAtIndex:2]];
}

and use it by doing:

NSArray *argArray = [NSArray arrayWithObjects:string1,string2,string3,nil];
[self performSelector:@selector(myFunctWrapper:) withObject:argArray afterDelay:1.0];

This way I can have multiple arguments and use the selector with delay.

aqua
  • 3,269
  • 28
  • 41
  • 1
    Those things you call functions are actually called methods. Not only in Objective-C, but in all serious programming languages this distinction is made. To be precise: as soon as this thing is attached to an object, it's a method. Standing alone it's called a function. – Jacque Mar 02 '13 at 09:35
  • For the general case I tend to use a dictionary to pass the parms. You're not order-dependent and you can easily have optional parms, etc. – Hot Licks Jun 12 '14 at 12:25
5
@selector(printText:andMore:)
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
4

Another option is to use an even shorter syntax:

#import <objc/message.h> // objc_msgSend
...
((void (*)(id, SEL, Type1, Type2, Type3))objc_msgSend)(target, a_selector, obj1, obj_of_type2, obj_of_type3);
Yoav
  • 5,962
  • 5
  • 39
  • 61
4
[self performSelector:@selector(printText:andMore) withObject:@"Cake" withObject:@"More Cake"];
pheelicks
  • 7,461
  • 2
  • 45
  • 50
  • 1
    There is `-performSelector:withObject:withObject:` and `-performSelector:withObject:afterDelay:`, but the mix of the two does not exist. – kennytm Feb 19 '10 at 17:50
  • Right you are. I was copy/pasting and made a mistake. Good spot – pheelicks Feb 19 '10 at 18:11
2

Elaborating on Ben-Uri's answer, which can be written way shorter.

E.g. calling the UIView method - (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view can be done as follows:

SEL selector = @selector(covertPoint:toView:);
IMP method = [viewA methodForSelector:selector];
CGPoint pointInB = method(viewA, selector, pointInA, viewB);
Community
  • 1
  • 1
fphilipe
  • 9,739
  • 1
  • 40
  • 52
  • Based on this SO post (http://stackoverflow.com/questions/10952996/why-does-this-code-give-exc-bad-access-using-imp) you will have memory problems done this way if you have ARC enabled. – stuckj Aug 05 '14 at 22:01
1

As KennyTM pointed out, the selector syntax is

@selector(printText:andMore:)

You call it with

performSelector:withObject:withObject. 

... if you need more arguments or different types, you need to use NSIvocation

VoidPointer
  • 17,651
  • 15
  • 54
  • 58
1

Using NSInvocation as you specify you can create an NSObject category that implements

- (void)performSelector:(SEL)aSelector withObjects:(NSArray *)arguments;

Something like:

- (void)performSelector:(SEL)aSelector withObjects:(NSArray *)arguments
{
    NSMethodSignature *signature = [self methodSignatureForSelector: aSelector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: signature];
    [invocation setSelector: aSelector];

    int index = 2; //
    for (NSObject *argument in arguments) {
        [invocation setArgument: &argument atIndex: index];
        index ++;
    }
    [invocation invokeWithTarget: self];
}

from: iOS - How to implement a performSelector with multiple arguments and with afterDelay?

Kappe
  • 9,217
  • 2
  • 29
  • 41