Objective-C:使用多个参数调用选择器

问题描述 投票:0回答:9

在MyClass.m中,我定义了

- (void) myTest: (NSString *) withAString{
    NSLog(@"hi, %@", withAString);
}

以及 MyClass.h 中的适当声明。稍后我想打电话

[self performSelector:@selector(mytest:withAString:) withObject: mystring];

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

我尝试了一个更简单的情况,其中一个选择器不带任何参数,将字符串打印到控制台,并且工作得很好。代码有什么问题以及如何修复它?谢谢。

objective-c selector
9个回答
315
投票

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

  1. 方法的名称(在本例中为“myTest”)(必需)
  2. 如果方法有输入,则方法名称后面有“:”(冒号)。
  3. 每个附加输入的名称和“:”。

选择器不知道:

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

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

@implementation ClassForSelectors
- (void) fooNoInputs {
    NSLog(@"Does nothing");
}
- (void) fooOneInput:(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

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

SEL myTestSelector = @selector(myTest:);

138
投票

您的方法签名是:

- (void) myTest:(NSString *)

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

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

[self performSelector:@selector(myTest:) withObject:myString];

它会起作用的。

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

- (void)myTestWithAString:(NSString*)aString;

并致电:

[self performSelector:@selector(myTestWithAString:) withObject:myString];

14
投票

@肖恩·阿尼

performSelector:withObject:withObject:

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

performSelector:withObject:afterDelay:)

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


7
投票

您的代码有两个问题。其中一个已被识别并得到答复,但另一个则没有。首先是您的选择器缺少其参数名称。然而,即使您解决了这个问题,假设您修改后的方法签名仍然包含多个参数,该行仍然会引发异常。假设您修改后的方法声明为:

-(void)myTestWithString:(NSString *)sourceString comparedTo:(NSString *)testString ;

为采用多个参数的方法创建选择器是完全有效的(例如 @selector(myTestWithString:comparedTo:) )。然而,performSelector 方法只允许您向 myTest 传递一个值,不幸的是 myTest 有多个参数。它会出错并告诉您没有提供足够的值。

您始终可以重新定义方法以将集合作为唯一参数:

-(void)myTestWithObjects:(NSDictionary *)testObjects ;

但是,有一个更优雅的解决方案(不需要重构)。答案是使用 NSInvocation 及其

setArgument:atIndex:
invoke
方法。

我已经写了一篇文章,包括一个代码示例,如果您想了解更多详细信息。重点是线程,但基础知识仍然适用。

祝你好运!


3
投票

你的方法签名毫无意义,你确定这不是一个拼写错误吗?我不清楚它是如何编译的,尽管也许您收到了您忽略的警告?

您希望该方法采用多少个参数?


2
投票

认为类应该定义为:

- (void) myTestWithSomeString:(NSString *) astring{
    NSLog(@"hi, %s", astring);
}

你只有一个参数,所以你应该只有一个:

您可能还想考虑在 NSLog 中使用 %@ - 这只是一个好习惯 - 然后会写出任何对象 - 而不仅仅是字符串。


0
投票

Android 开发者于 2021_10_15 发布。

(OC比较难用,-_-||)

使用多个参数调用选择器,

您可以使用

NSObject performSelector:withObject:withObject

但只支持传递两个参数!!!

幸运的是,你可以通过

performSelector withObject X 3
函数来实现你的
objc_msgSend

#include <objc/message.h>

- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2 withObject:(id)object3 {
    typedef id (*send_type)(id, SEL, id, id, id);
    send_type func = (send_type) objc_msgSend;
    id retValue = func(self, aSelector, object1, object2, object3);
    return retValue;
}

用途:

- (NSString *)ObjcMsgSendWithString:(NSString *)string withNum:(NSNumber *)number withArray:(NSArray *)array {
    NSLog(@" ---> %@, %@, %@", string, number, array[0]);
    return @"return 311";
}

- (void)test{
    NSString *str = @"字符串objc_msgSend";
    NSNumber *num = @20;
    NSArray *arr = @[@"数组值1", @"数组值2"];

    SEL sel = @selector(ObjcMsgSendWithString:withNum:withArray:);
    NSLog(@"1223 ---> %@", [self performSelector:sel withObject:str withObject:num withObject:arr]);
}

0
投票

正确的语法是

[self performSelector: @selector(mytest:) withObject: mystring];

我在这里看到一条评论,内容是关于如何不能传递对象以外的参数,以及苹果如何限制为两个。您可以使用

methodForSelector
方法来绕过这些限制。

SEL sel = @selector(mytest:);

(*[self methodForSelector: sel])(self, sel, mystring);

取消引用是可选的。因此,

[self methodForSelector: sel](self, sel, mystring);

也有效。


-1
投票

iOS 用户还期望自动大写:在标准文本字段中, 区分大小写的语言中句子的第一个字母是 自动大写。

您可以决定是否实现此类功能;没有 针对刚刚列出的任何功能的专用 API,因此提供它们 是一种竞争优势。

Apple 文档称,自定义键盘中没有可用于此功能和其他一些预期功能的 API。所以你需要找出你自己的逻辑来实现这个。

© www.soinside.com 2019 - 2024. All rights reserved.