Swift 3.2 中的 UIControl addTarget(_:action:for:) 方法

UIControl addTarget(_:action:for:) Method in Swift 3.2

我有一个 class,我们称它为 ClassA,使用以下 addTarget 调用。

awesomeBtn.addTarget(nil, action: #selector(awesomeMethod), for: .touchUpInside)

当方法 awesomeMethod 在 ClassA 中时,编译器接受以上行(即与 addTarget 调用相同 class)。

但是,如果 awesomeMethod 不在 ClassA 中,假设它在 ClassB 中,那么编译器会报错,我不得不在操作中指定 class 名称。

awesomeBtn.addTarget(nil, action: #selector(ClassB.awesomeMethod), for: .touchUpInside)

在以前的Swift版本中(不确定是哪个版本),我可以简单地写下下面的内容,不管哪个class包含这个方法。

awesomeBtn.addTarget(nil, action:("awesomeMethod"), forControlEvents:.touchUpInside)

想了解这是为什么,或者我是否做错了什么,谢谢。

是的,他们将其从 String 更改为 String,如果您输入错误的方法名称,这只是一个运行时错误,#selector 会强制对该方法进行编译时检查。他们只是想尽早发现您的错误。

However, if awesomeMethod is NOT in ClassA, let's say it's in ClassB, then the compiler complains and I am forced to specify the class name in the action.

不,您可以指定一个实现方法的@objc protocol

@objc protocol AwesomeProtocol {
    func awesomeMethod()
}

然后,即使您的 class 没有实现该方法,您也可以指定:

awesomeBtn.addTarget(nil, action: #selector(AwesomeProtocol.awesomeMethod), for: .touchUpInside)

注意:似乎没有必要让任何人都采用该协议。该按钮向上搜索响应者链并使用它找到的第一个匹配方法。虽然,您应该通过实现 awesomeMethod 的任何 class 采用该协议,以便 Swift 可以在编译时检测方法签名中的错误。

基本上,Swift对你很好 :)

在旧版本中,您使用字符串文字或 Selector(...) 语法来编写选择器。这样做的缺点是没有编译时检查选择器是否存在。如果你在某处有错字,你只能在运行时发现。您不需要封闭的 class 的名称,因为选择器只是方法的名称,不包括封闭的 class。在运行时,按钮自己的逻辑将在您传递的 target 对象中找到该选择器。没有人对 class 选择器所在的位置给出一个 f**k。选择器要么存在于 target 对象中,要么不存在。

现在 Swift 已经改进并提供了 #selector 用于编写选择器的语法。 检查选择器是否有效。编译器需要查找同名方法的声明。这就是为什么您必须添加 class 名称以便编译器知道在哪里查找的原因。否则它只会在当前 class 中查找它。然而,这并不意味着选择器在运行时会一直存在,因为在运行时,按钮会检查 target 是否有那个选择器,所以如果你传入另一​​个 class 的选择器(不是target) 的class),按钮仍然找不到选择器。