@selector中的参数

35

有没有办法在选择器中传递参数?

示例: 我有这个方法:

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

}

我需要通过一个选择器传递两个参数来调用这个函数。

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

我该怎么做?

5个回答

56

您可以使用NSTimer方法:

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

相反地,由于NSInvocation对象允许您传递参数; 正如文档所定义的那样,NSInvocation对象是一个静态的Objective-C消息,即将操作转换为对象。

而使用选择器创建NSTimer对象需要方法的格式为:

- (void)timerFireMethod:(NSTimer*)theTimer

使用NSInvocation,你可以设置目标、选择器和传递的参数:

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];

instanceMethodSignatureForSelector: 是在 NSObject 中声明的方便函数,用于返回一个 NSMethodSignature 对象,以便传递给 NSInvocation。其中 MyObject 是声明和实现 myMethod:setValue2: 的类。

另外需要注意的是,使用 setArgument:atIndex: 时,用于设置选择器方法参数的索引从2开始。文档中指出:

索引0和1表示隐藏参数 self 和 _cmd,应使用 setTarget: 和 setSelector: 方法直接设置这些值。使用2或更大的索引可用于通常作为消息传递的参数。


1
这是一个比我的更好的答案。你应该这样做以避免在你的实现中添加不必要的方法。 - Matt Ball
3
请注意,如果str1str2没有静态分配,那么它们将会泄漏。 NSTimer文档中指出它会向其调用对象发送-retainArguments消息,但您也可以自己向调用对象发送-retainArguments消息,问题在于需要先保留参数,然后告诉调用对象去保留它们。不要自己保留调用参数!让调用对象自己处理 - 在这里,让计时器自己处理即可。 - Jeremy W. Sherman
3
在检索方法签名时,请勿硬编码targetInstance的类,就像之前所做的那样。 如果您真的想使用+instanceMethodSignatureForSelector:,可以使用[[targetInstance class] instanceMethodSignatureForSelector:selector],但这太过复杂了,只需使用[targetInstance methodSignatureForSelector:selector]向对象本身请求方法签名即可。 - Jeremy W. Sherman

27

scheduledTimerWithTimeInterval:方法中,您传递的选择器只能有一个参数。此外,它的一个参数必须是一个NSTimer *对象。换句话说,选择器必须采用以下形式:

- (void)timerFireMethod:(NSTimer*)theTimer
你可以将参数存储在`userInfo`字典中,并从计时器回调函数中调用所需的选择器:

您可以将参数存储在userInfo字典中,并从计时器回调函数中调用所需的选择器:

- (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];
}

2
这对于你上面发布的特定方法是正确的,isiaatz。如果那只是一个例子,并且你想知道如何向选择器发送多个参数,请修改你的问题。 - h4xxr
我喜欢这个答案比被接受的答案更简洁。 - Karsten Silz

2
看起来需要使用块(假设这是针对Snow Leopard的)。-jcr

0

现在看来,块似乎是一个显而易见的答案...但在经典运行时中还有另一种惯用语,即将您的参数拼成一个单一的对象:

 - (void)doMyMethod:(NSDictionary *)userInfo
 {
     [self myMethod: [userInfo objectForKey:@"value1"] setValue2: [userInfo objectForKey:@"value2"]];
 }
 - (void)myMethod:(NSString*)value1 setValue2:(NSString*)value2{

}

你现在可以分派到

[self performSelector:@selector(doMyMethod:) withObject:@{@"value1":@"value1",@"value2":@"value2"}];

-2
@selector(myMethod:setValue2:)

由于您的方法选择器不仅仅是叫做myMethod,而是myMethod:setValue2:

此外(我可能有点离谱),我相信在技术上您可以省略冒号之间的单词,因此也可以使用@selector(myMethod::),但除非其他人可以确认,否则请不要引用我。


2
那是不正确的。选择器是方法的完整名称,包括冒号之间的所有内容。myMethod::myMethod:setValue2:是不同的选择器,除非您进行了一些运行时魔法,否则它们将与不同的实现相关联。 - Jeremy W. Sherman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接