如何解决“使用不明确"的问题?使用 Swift #selector 语法编译错误? [英] How do I resolve "ambiguous use of" compile error with Swift #selector syntax?

查看:14
本文介绍了如何解决“使用不明确"的问题?使用 Swift #selector 语法编译错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

[注意 这个问题最初是在 Swift 2.2 下制定的.它已针对 Swift 4 进行了修订,涉及两个重要的语言更改:第一个方法参数 external 不再被自动抑制,选择器必须显式暴露给 Objective-C.]

[NOTE This question was originally formulated under Swift 2.2. It has been revised for Swift 4, involving two important language changes: the first method parameter external is no longer automatically suppressed, and a selector must be explicitly exposed to Objective-C.]

假设我的班级中有这两种方法:

Let's say I have these two methods in my class:

@objc func test() {}
@objc func test(_ sender:AnyObject?) {}

现在我想使用 Swift 2.2 的新 #selector 语法来制作一个选择器对应于这些方法中的 firstfunc test().我该怎么做?当我尝试这个时:

Now I want to use Swift 2.2's new #selector syntax to make a selector corresponding to the first of these methods, func test(). How do I do it? When I try this:

let selector = #selector(test) // error

...我收到错误消息test() 的使用不明确."但如果我这样说:

... I get an error, "Ambiguous use of test()." But if I say this:

let selector = #selector(test(_:)) // ok, but...

...错误消失了,但我现在指的是错误的方法,一个带有参数的方法.我想引用一个 没有 任何参数.我该怎么做?

... the error goes away, but I'm now referring to the wrong method, the one with a parameter. I want to refer to the one without any parameter. How do I do it?

[注:例子不是人为的.NSObject 有 Objective-C 的 copycopy: 实例方法,Swift copy()copy(sender:AnyObject?);所以这个问题在现实生活中很容易出现.]

[Note: the example is not artificial. NSObject has both Objective-C copy and copy: instance methods, Swift copy() and copy(sender:AnyObject?); so the problem can easily arise in real life.]

推荐答案

[注意 这个答案最初是在 Swift 2.2 下制定的.它已针对 Swift 4 进行了修订,涉及两个重要的语言更改:第一个方法参数 external 不再被自动抑制,选择器必须显式暴露给 Objective-C.]

[NOTE This answer was originally formulated under Swift 2.2. It has been revised for Swift 4, involving two important language changes: the first method parameter external is no longer automatically suppressed, and a selector must be explicitly exposed to Objective-C.]

您可以通过将函数引用转换到正确的方法签名来解决此问题:

You can work around this problem by casting your function reference to the correct method signature:

let selector = #selector(test as () -> Void)

(但是,在我看来,你不应该这样做.我认为这种情况是一个错误,表明 Swift 引用函数的语法不充分.我提交了错误报告,但无济于事.)

(However, in my opinion, you should not have to do this. I regard this situation as a bug, revealing that Swift's syntax for referring to functions is inadequate. I filed a bug report, but to no avail.)

只是总结一下新的#selector语法:

Just to summarize the new #selector syntax:

此语法的目的是防止在将选择器作为文字字符串提供时可能出现的非常常见的运行时崩溃(通常是无法识别的选择器").#selector() 接受一个函数引用,编译器将检查该函数是否确实存在,并为您解析对Objective-C 选择器的引用.因此,您不能轻易犯任何错误.

The purpose of this syntax is to prevent the all-too-common runtime crashes (typically "unrecognized selector") that can arise when supplying a selector as a literal string. #selector() takes a function reference, and the compiler will check that the function really exists and will resolve the reference to an Objective-C selector for you. Thus, you can't readily make any mistake.

(好的,是的,你可以.你可以是一个完整的笨蛋,并将目标设置为不实现 #selector 指定的操作消息的实例代码>.编译器不会阻止你,你会像过去一样崩溃.叹息......)

( Okay, yes you can. You can be a complete lunkhead and set the target to an instance that doesn't implement the action message specified by the #selector. The compiler won't stop you and you'll crash just like in the good old days. Sigh...)

函数引用可以以三种形式出现:

A function reference can appear in any of three forms:

  • 函数的裸名.如果函数是明确的,这就足够了.因此,例如:

  • The bare name of the function. This is sufficient if the function is unambiguous. Thus, for example:

@objc func test(_ sender:AnyObject?) {}
func makeSelector() {
    let selector = #selector(test)
}

只有一个 test 方法,所以这个 #selector 引用它,即使它带有一个参数而 #selector 没有不提参数.在幕后解析的 Objective-C 选择器仍然正确地是 "test:"(带有冒号,表示参数).

There is only one test method, so this #selector refers to it even though it takes a parameter and the #selector doesn't mention the parameter. The resolved Objective-C selector, behind the scenes, will still correctly be "test:" (with the colon, indicating a parameter).

函数的名称以及其签名的其余部分.例如:

The name of the function along with the rest of its signature. For example:

func test() {}
func test(_ sender:AnyObject?) {}
func makeSelector() {
    let selector = #selector(test(_:))
}

我们有两个test方法,所以需要区分;符号 test(_:) 解析为 second 一个,即带有参数的那个.

We have two test methods, so we need to differentiate; the notation test(_:) resolves to the second one, the one with a parameter.

函数的名称,带或不带其签名的其余部分,加上强制转换以显示参数的类型.因此:

The name of the function with or without the rest of its signature, plus a cast to show the types of the parameters. Thus:

@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不允许重载,所以只有一个暴露,我们可以只为暴露的形成一个选择器,因为选择器是 Objective-C 的一个特性.但就 Swift 而言,我们必须仍然消除歧义,而演员阵容就是这样做的.

Here, we have overloaded test(_:). The overloading cannot be exposed to Objective-C, because Objective-C doesn't permit overloading, so only one of them is exposed, and we can form a selector only for the one that is exposed, because selectors are an Objective-C feature. But we must still disambiguate as far as Swift is concerned, and the cast does that.

(在我看来,正是这种语言特征被滥用——作为上述答案的基础.)

(It is this linguistic feature that is used — misused, in my opinion — as the basis of the answer above.)

另外,您可能必须通过告诉 Swift 函数所在的类来帮助 Swift 解析函数引用:

Also, you might have to help Swift resolve the function reference by telling it what class the function is in:

  • 如果类与这个类相同,或者在这个类的超类链上,通常不需要进一步的解析(如上面的示例所示);可选地,您可以说 self,使用点表示法(例如 #selector(self.test),在某些情况下您可能必须这样做.

  • If the class is the same as this one, or up the superclass chain from this one, no further resolution is usually needed (as shown in the examples above); optionally, you can say self, with dot-notation (e.g. #selector(self.test), and in some situations you might have to do so.

否则,您可以使用对实现该方法的 instance 的引用,使用点表示法,就像在这个现实生活中的示例中一样(self.mp 是一个 MPMusicPlayerController):

Otherwise, you use either a reference to an instance for which the method is implemented, with dot-notation, as in this real-life example (self.mp is an MPMusicPlayerController):

let pause = UIBarButtonItem(barButtonSystemItem: .pause, 
    target: self.mp, action: #selector(self.mp.pause))

...或者您可以使用 类的名称,带有点符号:

...or you can use the name of the class, with dot-notation:

class ClassA : NSObject {
    @objc func test() {}
}
class ClassB {
    func makeSelector() {
        let selector = #selector(ClassA.test)
    }
}

(这似乎是一个奇怪的符号,因为它看起来像你说 test 是一个类方法而不是一个实例方法,但它仍然会被正确地解析为一个选择器,仅此而已这很重要.)

(This seems a curious notation, because it looks like you're saying test is a class method rather than an instance method, but it will be correctly resolved to a selector nonetheless, which is all that matters.)

这篇关于如何解决“使用不明确"的问题?使用 Swift #selector 语法编译错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆