Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SEL performSelector and arguments

It seems like there should be an easy way to call a selector with some arguments when all you have is a SEL object. I can't seem to find the correct syntax.

-(MyClass*) init: (SEL)sel owner:(NSObject*) parent
{
   int i =10;
   [parent performSelector:sel:i  ];
}
like image 935
madmik3 Avatar asked Apr 26 '10 19:04

madmik3


5 Answers

Take a look at the NSObject documentation. In this case:

[parent performSelector:sel withObject:[NSNumber numberWithInt:i]];

(note this method is actually listed in the NSObject protocol documentation). Since -[NSObject performSelector:withObject:] requires an object argument, you will have to write a wrapper in parent's class like

-(void)myMethodForNumber:(NSNumber*)number {
    [self myMethod:[number intValue]];
}

to unbox the NSNumber.

If you really want to invoke a method that takes non-object arguments directly (for example, you don't have control of the callee source and don't want to add a category), you can use NSInvocation:

NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[parent methodSignatureForSelector:sel]];
[inv setSelector:sel];
[inv setTarget:parent];
[inv setArgument:&i atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
[inv invoke];

On a side note, your method looks like an init method, but does not follow the correct initializer pattern for Objective-C. You need to call the super-classes initializer, and you need to test for a nil result from that call and you must return self from the initializer method. In all cases, your Objective-C initializer methods should look like:

-(id)myInitMethod {
    self = [super init];
    if(self != nil) {
      //perform initialization of self
    }

    return self;
}

Your method (if it's an init method) would then look like:

-(id) init: (SEL)sel owner:(NSObject*) parent
{
   self = [super init];
   if(self != nil) {
       int i = 10;
       NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[parent methodSignatureForSelector:sel]];
       [inv setSelector:sel];
       [inv setTarget:parent];
       [inv setArgument:&i atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
       [inv invoke];
   }

    return self;
}

To be more Objective-C stylistically, I would rename the initializer -(id)initWithSelector:owner: as well.

like image 117
Barry Wark Avatar answered Nov 19 '22 13:11

Barry Wark


what Barry Wark said is great.. i have just modified the discussion for lazy programmers

-(void)myMethodWith:(int)number andBOOL:(BOOL) someBool andStr:(NSString *)str{
    NSLog(@"%d %d %@",number,someBool,str);
}

-(void) testMethod{
    SEL sel = @selector(myMethodWith:andBOOL:andStr:);
    int i = 10;
    BOOL bol = YES;
    NSString *str = @"hey baby !";
    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:sel]];
    [inv setSelector:sel];
    [inv setTarget:self];
    [inv setArgument:&i atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
    [inv setArgument:&bol atIndex:3];
    [inv setArgument:&str atIndex:4];
    [inv invoke];
}
like image 33
yunas Avatar answered Nov 19 '22 13:11

yunas


You want to use performSelector:withObject: The tricky part is converting the int to an NSObject. You cannot use performSelector with messages that take int params, it must instead take an id.

From the NSObject Protocol Reference:

aSelector should identify a method that takes a single argument of type id. For methods with other argument types and return values, use NSInvocation.

Once that change is made, you can do:

id arg = [NSNumber numberWithInt:10];    
[parent performSelector:sel withObject:arg];
like image 21
Frank Krueger Avatar answered Nov 19 '22 11:11

Frank Krueger


You can use:

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

Or if you need to use more complex method use NSInvocation class

like image 21
Skie Avatar answered Nov 19 '22 11:11

Skie


For methods that take one or two objects of type id as arguments, you can use:

[parent performSelector:sel withObject:argument1];

or

[parent performSelector:sel withObject:argument1 withObject:argument2];

For methods with other argument types, create an NSInvocation that can encapsulate arbitrary method calls.

like image 24
Ole Begemann Avatar answered Nov 19 '22 13:11

Ole Begemann