views:

3965

answers:

5

Is there any way that I can pass arguments in selector?

example:

I have this method

- (void)myMethod:(NSString*)value1 setValue2:(NSString*)value2{

}

and I need to call this function through a selector passing two arguments.

[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(/*my method*/) userInfo:nil repeats:YES];

How can I do this?

A: 
@selector(myMethod:setValue2:)

Since the selector for your method isn't just called myMethod but instead myMethod:setValue2:.

Also (and I could be off base here), I believe technically you can drop the words between colons and thus also use @selector(myMethod::) but don't quote me on this unless others can confirm it.

jbrennan
That is incorrect. The selector is the entire name of the method, including everything between the colons. `myMethod::` and `myMethod:setValue2:` are distinct selectors, which would be associated with distinct implementations unless you've worked some runtime magic.
Jeremy W. Sherman
OK. I was off base.
jbrennan
+8  A: 

For scheduledTimerWithTimeInterval:, the selector you pass can only have one argument. Further, its one argument must be an NSTimer * object. In other words, the selector must take the following form:

- (void)timerFireMethod:(NSTimer*)theTimer

What you could do is store the arguments in the userInfo dictionary and call the selector you want from the timer callback:

- (void)startMyTimer {
    /* ... Some stuff ... */
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(callMyMethod:) userInfo:[NSDictionary dictionaryWithObjectsAndKeys:someValue, @"value1", someOtherValue, @"value2", nil] repeats:YES];
}

- (void)callMyMethod:(NSTimer *)theTimer {
    NSString *value1 = [[theTimer userInfo] objectForKey:@"value1"];
    NSString *value2 = [[theTimer userInfo] objectForKey:@"value2"];
    [self myMethod:value1 setValue2:value2];
}
Matt Ball
This is correct for the specific method you have posted above isiaatz. If that was just an example and you want to know in general how to send multiple arguments to a selector please amend your question
h4xxr
I like this better than the accepted answer since it's more concise.
Karsten Silz
I prefer this better also
Jami
+14  A: 

You could use the NSTimer method:

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds
                                 invocation:(NSInvocation *)invocation
                                    repeats:(BOOL)repeats;

Instead, since an NSInvocation object will allow you to pass arguments; an NSInvocation object is, as the docs define it:

an Objective-C message rendered static, that is, it is an action turned into an object.

Whilst creating an NSTimer object using a selector requires the format of the method being:

- (void)timerFireMethod:(NSTimer*)theTimer

An NSInvocation allows you to set the target, the selector, and the arguments that you pass in:

SEL selector = @selector(myMethod:setValue2:);

NSMethodSignature *signature = [MyObject instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:selector];

NSString *str1 = @"someString";
NSString *str2 = @"someOtherString";

//The invocation object must retain its arguments
[str1 retain];
[str2 retain];

//Set the arguments
[invocation setTarget:targetInstance];
[invocation setArgument:&str1 atIndex:2];
[invocation setArgument:&str2 atIndex:3];

[NSTimer scheduledTimerWithTimeInterval:0.1 invocation:invocation repeats:YES];

Where MyObject is the class that myMethod:setValue2: is declared and implemented on – instanceMethodSignatureForSelector: is a convenience function declared on NSObject which returns an NSMethodSignature object for you, to be passed to NSInvocation.

Also, to note, with setArgument:atIndex:, the indices for arguments to be passed to the method set as the selector start at index 2. From the docs:

Indices 0 and 1 indicate the hidden arguments self and _cmd, respectively; you should set these values directly with the setTarget: and setSelector: methods. Use indices 2 and greater for the arguments normally passed in a message.

Perspx
This is a better answer than mine. You should do this to avoid polluting your implementations with unnecessary methods.
Matt Ball
Note that, were `str1` and `str2` not statically allocated, you'd be leaking them. `NSTimer` is documented as sending `-retainArguments` to its invocation object, but it would not hurt for you to send `-retainArguments` to the invocation object yourself. The problem is retaining the arguments yourself, then telling the invocation to retain them, as well.Don't retain invocation arguments yourself! Let the invocation handle it – and, here, let the timer handle it itself.
Jeremy W. Sherman
Don't hardcode the class of `targetInstance`, as was done in retrieving the method signature. If you really wanted to use `+instanceMethodSignatureForSelector:`, you could use `[[targetInstance class] instanceMethodSignatureForSelector:selector]`, but that's needlessly complex – just ask the object itself for the method signature using `[targetInstance methodSignatureForSelector:selector]`.
Jeremy W. Sherman
+1  A: 

Looks like a job for blocks (assuming this is targeted for Snow Leopard.)

-jcr

NSResponder
A: 

Thanks Perspx for the answers! :* :*

outis
outis: A user with less than 50 karma cannot do that. Either way, the correct way to express thanks for a helpful answer is to upvote it.
Peter Hosey
Can't any user post comments on questions they asked? Good point about the upvoting.
outis