Objective-C:调用具有多个参数的选择器

Stu*_*Stu 139 objective-c selector

在MyClass.m中,我定义了

- (void) myTest: (NSString *) withAString{
    NSLog(@"hi, %@", withAString);
}
Run Code Online (Sandbox Code Playgroud)

和MyClass.h中的适当声明.后来我打电话来

[self performSelector:@selector(mytest:withAString:) withObject: mystring];
Run Code Online (Sandbox Code Playgroud)

在MyClass.m但我得到一个类似于*终止应用程序的错误, 因为未捕获的异常'NSInvalidArgumentException',原因:'* - [MyClass myTest:withAtring:]:无法识别的选择器发送到实例0xe421f0'

我尝试了一个更简单的情况,一个选择器没有参数,打印一个字符串到控制台,并且工作得很好.代码有什么问题,我该如何解决?谢谢.

Sha*_*ney 311

在Objective-C中,选择器的签名包括:

  1. 方法的名称(在这种情况下,它将是'myTest')(必需)
  2. 如果方法有输入,则在方法名后面加一个':'(冒号).
  3. 每个附加输入的名称和":".

选择者不知道:

  1. 输入类型
  2. 方法的返回类型.

这是一个类实现,其中performMethodsViaSelectors方法通过选择器执行其他类方法:

@implementation ClassForSelectors
- (void) fooNoInputs {
    NSLog(@"Does nothing");
}
- (void) fooOneIput:(NSString*) first {
    NSLog(@"Logs %@", first);
}
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
    NSLog(@"Logs %@ then %@", first, second);
}
- (void) performMethodsViaSelectors {
    [self performSelector:@selector(fooNoInputs)];
    [self performSelector:@selector(fooOneInput:) withObject:@"first"];
    [self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second"];
}
@end
Run Code Online (Sandbox Code Playgroud)

您要为其创建选择器的方法具有单个输入,因此您将为其创建一个选择器,如下所示:

SEL myTestSelector = @selector(myTest:);
Run Code Online (Sandbox Code Playgroud)

  • +100:太棒了!我不知道能够使用多个"withObject:"参数.如果可以的话,我会赞成这一百次...... (30认同)
  • 好答案.为了略微澄清,你的选择器名称必须至少有一个部分,可能会或不会采取参数 - 如果是,它必须有一个冒号.具有两个或多个部分的选择器名称必须在每个部分之后具有冒号 - 具有"-useFoo:andBar:toDoSomething"形式的选择器是不合法的. (3认同)

Lyn*_*son 135

您的方法签名是:

- (void) myTest:(NSString *)
Run Code Online (Sandbox Code Playgroud)

withAString恰好是参数(名称具有误导性,看起来它是选择器签名的一部分).

如果以这种方式调用该函数:

[self performSelector:@selector(myTest:) withObject:myString];
Run Code Online (Sandbox Code Playgroud)

它会工作.

但是,正如其他海报所建议的那样,您可能想要重命名该方法:

- (void)myTestWithAString:(NSString*)aString;
Run Code Online (Sandbox Code Playgroud)

并致电:

[self performSelector:@selector(myTestWithAString:) withObject:myString];
Run Code Online (Sandbox Code Playgroud)

  • 现在,我看到人们从这个答案中受益,我已经回顾了我的回答; 我建议调用简单: - (void)testWithString:(NSString*)aString; (2认同)

Lir*_*rik 13

@Shane Arney

performSelector:withObject:withObject:
Run Code Online (Sandbox Code Playgroud)

您可能还想提一下,此方法仅用于传递最多2个参数,并且不能延迟.(如performSelector:withObject:afterDelay:).

有点奇怪,苹果只支持2个对象发送,并没有使它更通用.

  • 谢谢(你的)信息.我无法延迟工作,现在我知道为什么.仅供参考,为了克服两个对象的限制,我传递了一个数组,然后在方法中使用它. (2认同)

Zac*_*ack 7

您的代码有两个问题.一个被确认并回答,但另一个没有.第一个是你的选择器缺少其参数的名称.但是,即使您修复了该行,该行仍将引发异常,假设您修改的方法签名仍包含多个参数.假设您修改后的方法声明为:

-(void)myTestWithString:(NSString *)sourceString comparedTo:(NSString *)testString ;
Run Code Online (Sandbox Code Playgroud)

为具有多个参数的方法创建选择器是完全有效的(例如@selector(myTestWithString:comparisonTo :)).但是,performSelector方法只允许您将一个值传递给myTest,不幸的是,myTest有多个参数.它会报错并告诉您没有提供足够的值.

您可以随时重新定义您的方法以获取集合,因为它是唯一的参数:

-(void)myTestWithObjects:(NSDictionary *)testObjects ;
Run Code Online (Sandbox Code Playgroud)

但是,有一个更优雅的解决方案(不需要重构).答案是使用NSInvocation的,与它一起setArgument:atIndex:invoke方法.

如果你想了解更多细节,我已经写了一篇文章,包括一个代码示例.重点是线程,但基础仍然适用.

祝好运!