如何解决“模糊使用”与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的copycopy:实例方法,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如此,这是重要的。