编译器错误:具有Objective-C选择器的方法与具有相同Objective-C选择器的先前声明冲突


209

我开始学习Swift,并且一直在YouTube上观看斯坦福大学的精彩视频讲座。如果您有兴趣或有帮助,可以使用以下链接(尽管不需要了解我的问题):

使用Swift开发iOS 8应用-2.更多Xcode和Swift,MVC

在听完讲座后,我发现(据我所知)我的代码与视频中的代码相同,但是在我的系统上,我遇到了编译器错误。经过大量的试验和错误,我设法将代码简化为两个示例,其中一个生成错误,另一个生成或未生成错误,但是我不知道是什么真正导致了错误或如何解决该错误。

产生错误的代码是:

import UIKit

class BugViewController: UIViewController
{
    func perform(operation: (Double) -> Double) {
    }

    func perform(operation: (Double, Double) -> Double) {
    }
}

这将产生以下编译器错误:

使用Objective-C选择器的“执行”方法“执行:”与使用相同Objective-C选择器的先前声明冲突

通过简单地删除UIViewController的子类,代码可以编译:

import UIKit

class BugViewController
{
    func perform(operation: (Double) -> Double) {
    }

    func perform(operation: (Double, Double) -> Double) {
    }
}

其他一些可能不相关的信息:

  • 我最近升级到优胜美地。
  • 当我安装Xcode时,最终得到一个Beta版本(版本6.3(6D543q)),因为(如果我没有记错的话)这是我需要在OS X版本上运行的版本。

我半是希望这是编译器中的错误,因为否则对我来说这没有任何意义。任何帮助都非常感谢!


3
您可以在优胜美地上运行Xcode 6.2。您可以从应用程序商店下载它,它可以在Beta版本的系统上使用。我现在不建议在斯坦福大学上使用Xcode 6.3,因为它是beta版本,并且包含Swift 1.2,这与视频中使用的早期版本的Swift不同。
vacawama 2015年

2
4月5日,来自用户(feb)的(当前接受的)答案不再是最佳答案。相反,4月16日来自(James Zhang)的答案更加具体和正确。
phlebotinum

Answers:


144

Objective-C不支持方法重载,您必须使用其他方法名称。当您继承UIViewController时,您也继承了NSObject,并使该类可与Obj-C互操作。另一方面,Swift确实支持重载,这就是为什么在删除继承时它可以工作。


2
使用Objective-C SUPPORTS方法覆盖(带有(可抑制的)编译器警告,通知您有关已实现的重载的警告),Apple只是不想让您这样做,以保持其框架不被重载。我UIFont每天都在使用这种超负荷功能。
Michi 2015年

@polarwar在下面的答案是Swift 2的最佳答案:stackoverflow.com/a/31500740/144088
Crashalot

237

我本人也在参加斯坦福课程,并且在这里也停留了很长时间,但是经过一番搜索,我发现这里有一些东西:Xcode发行说明,其中提到了以下内容:

Swift 1.2严格检查@objc方法和初始化程序的基于类型的重载,这是Objective-C不支持的。

// Has the Objective-C selector "performOperation:".
func performOperation(op: NSOperation) { /* do something */ }
// Also has the selector "performOperation:".
func performOperation(fn: () -> Void) {
    self.performOperation(NSBlockOperation(block: fn))
}

从Swift调用时,此代码将起作用,但从Objective-C调用时,则很容易崩溃。要解决此问题,请使用Objective-C不支持的类型,以防止Swift编译器将成员暴露给Objective-C运行时:

  • 如果有意义,请将成员标记为私有以禁用@objc的推断。
  • 否则,请使用具有默认值的虚拟参数,例如:_ nonobjc:()=()。(19826275)

私有子类中公开给Objective-C的方法的覆盖不会推断为@objc,从而导致Swift编译器崩溃。将@objc属性显式添加到任何此类覆盖方法中。(19935352)

在使用Swift的项目或工作区中使用“快速打开”时,SDK的符号不可用。(20349540)

我所做的只是在覆盖方法之前添加“ private”,如下所示:

    private func performOperation(operation: Double -> Double) {
    if operandStack.count >= 1 {
        displayValue = operation(operandStack.removeLast())
        enter()
    }
}

3
该解决方案是最可行的,我觉得恕我直言,因为这完全是有道理的设置此方法私人
demental

38
请注意,现在还有一个@nonobjc属性,可用于从Objective-C运行时中排除方法。
Erik J

2
我在下面第二个@ErikJ的评论和Polarwar的答案。对于Swift 2和xcode 7来说,这似乎是最好的答案。如果您尚未更新,我强烈建议您这样做。
奥斯汀A

@polarwar在下面的答案是Swift 2的最佳答案:stackoverflow.com/a/31500740/144088
Crashalot

111

正如已经回答的那样,ObjC不支持方法重载(两个具有相同名称的方法),并且在Xcode 7下的swift 2中,有两个选项可以解决此类问题。一种选择是使用属性来重命名方法:@objc(newNameMethod:)

func methodOne(par1, par2) {...}

@objc(methodTwo:)
func methodOne(par1) {...}

解决Xcode 7+中此问题的另一种@nonobjc方法是将 属性应用于任何方法,下标或初始化程序

func methodOne() {...}

@nonobjc
func methodOne() {...}

6
这解决了快速2(及更高)的问题。应该更新为最正确的答案。ty。
Maxim Veksler 2015年

2
对于使用Swift 2和Xcode 7 +的任何人,这是我同意Polarwar的正确答案
TerNovi 2015年

17

问题UIViewController是一@objc类。从继承时UIViewControllerBugViewController也是一个@objc类。

这意味着它必须符合Objective-C选择器的规则(方法的名称)。方法func perform(operation: (Double) -> Double)func perform(operation: (Double, Double) -> Double)都具有相同的选择器@selector(perform:)。这是不允许的。

要解决此问题,请使用其他名称:like func perform1(operation: (Double) -> Double)func perform2(operation: (Double, Double) -> Double)


我认为处理此问题的最佳方法是为您的perform()方法赋予更多描述性名称。这些方法是做什么的?它们如何更改视图控制器的状态?查看其他UIViewController方法以感受一下方法命名的风格,或阅读方法名称在类中应具有表现力和唯一性


谢谢-这很好地回答了我的问题,正如您是第一个那样,我将其标记为正确。
奥义峰2015年

话虽如此,我仍然不明白为什么讲课中的代码能正常工作,因为我很确定它做了我的非编译代码!嗨,我会回头再仔细检查一遍。一定有所不同。
吉祥2015年

2
@Auspice对于用于视频的Xcode版本可能没有产生错误,但这仍然是一个问题。直到Xcode 6.3,编译器才能够检测到此警告。
Mick MacCallum 2015年

3
Paul Hegarty想要在这里演示函数“重载”(两个具有相同名称,但参数集不同的函数),因此他确实使用了相同的方法名称!仅在Swift中允许重载,而在Objective-C中则不允许。这就是为什么解决方案是删除继承表单UIViewController(这是一个Objective-C类)或将方法声明为私有的原因。这两个解决方案将在此处的其他答案中进行详细说明。
罗尼·韦伯斯

实际上,我在函数的前面使用了私有关键字。例如,private func performOperation(操作:Double-> Double){}和private func performOperation(操作:(Double,Double)-> Double){}在这里,我在PRIVATE的帮助下实现了方法重载。因为我只在ViewController.Swift中使用了它们。为什么编译器不说任何错误?
iTag


2

由于具有两个具有相同Obj-C签名的方法,因此出现相同的错误:

static func prepareForUpSyncing(obj : NSManagedObject!) -> Bool
static func prepareForUpSyncing(objs : [NSManagedObject]!) -> Bool

我不想将其中之一标记为@nonobjc,因为在运行时可能会发生无法预料的后果。(如果没有可能,有人可以纠正我)

通过使用Swift的外部参数名称功能(我使外部名称与本地名称相同)解决了第二种方法,从而有效地更改了Obj-c方法签名:

static func prepareForUpSyncing(objs objs : [NSManagedObject]!) -> Bool {
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.