如何解决“模糊使用”与Swift #selector语法编译错误?
假设我在课堂上有这两种方法:
func test() {} func test(sender:AnyObject?) {}
现在我想使用Swift 2.2的新的#selector
语法来创build一个与这些方法中的第一个 func test()
相对应的select器。 我该怎么做? 当我尝试这个:
let selector = #selector(test) // error
…我得到一个错误,“模糊使用test()
。 但是如果我这样说:
let selector = #selector(test(_:)) // ok, but...
…错误消失了,但我现在指的是错误的方法 ,一个参数。 我想参考一个没有任何参数。 我该怎么做?
[注:这个例子不是人造的。 NSObject有Objective-C的copy
和copy:
实例方法,Swift的copy()
和copy(sender:AnyObject?)
; 所以这个问题在现实生活中很容易出现。]
您可以通过将您的函数引用转换为正确的方法签名来解决此问题:
let selector = #selector(test as () -> Void)
(但是在我看来,你不应该这样做,我把这种情况看作是一个bug,说明Swift提到函数的语法是不够的,我提交了一个bug报告,但是没有用。
只是要总结一下新的#selector
语法:
此语法的目的是为了防止在将select器作为文字string提供时可能出现的所有太常见的运行时崩溃(通常是“无法识别的select器”)。 #selector()
接受函数引用 ,编译器将检查函数是否真的存在,并将parsing对您的Objective-Cselect器的引用。 因此,你不容易犯任何错误。
( 编辑:好的,你可以,你可以是一个完整的仓库,并将目标设置为一个没有实现#selector
指定的动作信息的实例,编译器不会阻止你,你会崩溃在过去的美好时光中叹息…)
函数引用可以以三种forms出现:
-
函数的名字 如果函数是明确的,这就足够了。 因此,例如:
func test(sender:AnyObject?) {} func makeSelector() { let selector = #selector(test) }
只有一个
test
方法,所以这个#selector
即使它带有一个参数,并且#selector
没有提到这个参数,也是#selector
它。 在幕后parsing的Objective-Cselect器仍将正确地为"test:"
(用冒号指示参数)。 -
该函数的名称以及其签名的其余部分。 例如:
func test() {} func test(sender:AnyObject?) {} func makeSelector() { let selector = #selector(test(_:)) }
我们有两种
test
方法,所以我们需要区分; 符号test(_:)
parsing为第二个,带有参数的testing。 -
该函数的名称有或没有其余的签名,加上一个强制转换来显示参数的types 。 从而:
@objc func test(integer:Int) {} @nonobjc func test(string:String) {} func makeSelector() { let selector1 = #selector(test as (Int) -> Void) // or: let selector2 = #selector(test(_:) as (Int) -> Void) }
在这里,我们已经超负荷
test(_:)
。 重载不能被暴露给Objective-C,因为Objective-C不允许重载,所以只有其中的一个被暴露,并且我们可以仅为暴露的那个形成一个select器,因为select器是一个Objective-C特性。 但是,就斯威夫特而言,我们仍然应该消除歧义,而演员也是这样做的。(在我看来,这种语言特征被滥用 – 作为上述答案的基础。)
另外,你可能需要通过告诉它函数在什么类中来帮助Swiftparsing函数引用:
-
如果这个类与这个类相同,或者从这个类的超类链中,通常不需要进一步的parsing(如上面的例子所示)。 可选地,你可以用点符号(例如
#selector(self.test)
来表示self
,在某些情况下,你可能需要这样做。 -
否则,就像在这个真实的例子(
self.mp
是一个MPMusicPlayerController)一样,使用一个实现了方法的实例的引用,用点符号表示:let pause = UIBarButtonItem(barButtonSystemItem: .Pause, target: self.mp, action: #selector(self.mp.pause))
…或者你也可以用点符号来使用class级的名字:
class ClassA : NSObject { func test() {} } class ClassB { func makeSelector() { let selector = #selector(ClassA.test) } }
(这似乎是一个奇怪的符号,因为它看起来像你说的
test
是一个类的方法,而不是一个实例方法,但它会正确地解决select器,尽pipe如此,这是重要的。