当前位置: 首页 > 面试题库 >

如何使用Swift #selector语法解决“模棱两可的使用”编译错误?

南门正祥
2023-03-14
问题内容

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

假设我在课堂上有以下两种方法:

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

现在,我想使用Swift 2.2的新#selector语法创建一个与这些方法中的 一个相对应的选择器func test()。我该怎么做?当我尝试这个:

let selector = #selector(test) // error

…我收到一个错误消息:“的歧义使用” test()。但是如果我这样说:

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

…错误消失了,但是我现在指的是 错误的方法 ,即 带有 参数的方法。我想引用 不带 任何参数的那个。我该怎么做?

[注意:该示例不是人为的。NSObject同时具有Objective-C copycopy:实例方法Swift
copy()copy(sender:AnyObject?); 因此这个问题很容易在现实生活中出现。]


问题答案:

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

您可以解决这个问题,通过 铸造 你的函数引用正确的方法签名:

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

(但是,我认为您不必这样做。我认为这种情况是一个错误,表明Swift引用函数的语法不足。我提交了错误报告,但无济于事。)

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

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

编辑:
好的,是的。您可以成为一个完全笨拙的人,并将目标设置为一个实例,该实例未实现由所指定的操作消息#selector。编译器不会阻止您,并且您将崩溃,就像在过去的美好时光。

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

  • 函数的 裸名 。如果功能是明确的,这就足够了。因此,例如:
        @objc func test(_ sender:AnyObject?) {}
    func makeSelector() {
        let selector = #selector(test)
    }

只有一种test方法,因此#selector即使它需要一个参数并且#selector没有提到该参数,也要引用它。解析后的Objective-
C选择器在幕后仍将正确存在"test:"(带有冒号,指示一个参数)。

  • 函数的名称 及其签名 的其余部分。例如:
        func test() {}
    func test(_ sender:AnyObject?) {}
    func makeSelector() {
        let selector = #selector(test(_:))
    }

我们有两种test方法,因此我们需要区分;该表示法test(_:)解析为 第二 个,带有参数的表示法。

  • 带有或不带有其签名的其余部分的功能,再加上名 ,显示 类型 的参数。从而:
        @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而言,我们
仍然 必须消除歧义,而演员阵容就是这样做的。

(在我看来,正是这种语言功能被误用了,作为上述答案的基础。)

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

  • 如果该类与该类相同,或者在该类的超类链的上游,则通常不需要进一步的解决方法(如上例所示);您可以选择self使用点号(,例如)说出来,#selector(self.test)在某些情况下,您可能必须这样做。

  • 否则,您可以使用点标记来引用对其实现了该方法的 实例 ,如下面的真实示例self.mp是MPMusicPlayerController):

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

…或者您可以使用 类名 ,并使用点符号:

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

(这似乎是一种奇怪的表示法,因为您似乎在说的test是类方法而不是实例方法,但是尽管如此,它将正确地解析为选择器,这很重要。)



 类似资料:
  • 我应该如何编写以下Mockito匹配器,以便调用不会有歧义? 我试图在代码中模拟的实际函数调用是:

  • 无法找出正确的方法来使用匹配器来识别我要处理的exchange方法的重载。我正在打的电话:

  • 问题内容: 我有一个使用Xcode 10.3构建的VoiceSampler.framework 我正在尝试在新项目的Xcode11中使用该框架。我已经成功添加了该框架,但是当我用AppDelegate 编写时,出现以下错误: 使用Swift 5.0.1编译的模块无法由Swift 5.1编译器导入:/Users/apple/Projects/CaptureAppSwift/VoiceSampler.

  • 问题内容: 我正在尝试按照指令https://github.com/Paytm- Payments/Paytm_iOS_App_Kit/tree/master/Swift/BitCodeDisabled/PaytmNativeSDK将 第三方应用程序集成到项目中 我在构建中看到以下错误 Swift 5.0编译器无法导入使用Swift 4.2.1编译的模块 我尝试了工具链https://medium

  • 但是,当我尝试相同的示例时,通过将Integer更改为Object,代码编译得很好,输出为String 谁能帮助我理解为什么当输出来自其中有字符串的方法时,签名中有对象的方法是必需的。以及类型错误不明确的原因是什么。

  • 问题内容: 我尝试包含一个名为“ name”的类,但出现错误: 该类存在,并且不包含任何编译错误。 问题答案: 可能存在一些问题。 其中一类具有 测试目标, 而另一类则没有。您甚至必须将所有类都包含在测试目标中,或者不包含任何类。 如果是Objective C类,请检查该类是否在ObjectiveC桥接头文件中。 如果是NSManagedObject子类。在类声明之前添加。 如果它是另一个框架的一