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

Posted

技术标签:

【中文标题】编译器错误:使用 Objective-C 选择器的方法与先前使用相同 Objective-C 选择器的声明冲突【英文标题】:Compiler error: Method with Objective-C selector conflicts with previous declaration with the same Objective-C selector 【发布时间】:2015-06-10 01:13:00 【问题描述】:

我开始学习 Swift,并且一直在关注 YouTube 上非常好的斯坦福大学视频讲座。如果您有兴趣或有帮助,这里是一个链接(尽管不需要理解我的问题):

Developing ios 8 Apps with Swift - 2. More Xcode and Swift, MVC

在听完讲座后,我发现(据我所知)我的代码与视频中的代码相同,但在我的系统上出现了编译器错误。经过大量的试验和错误后,我设法将我的代码简化为两个示例,其中一个会产生错误,另一个或不会产生错误,但我不知道实际导致错误的原因或如何解决它。

产生错误的代码是:

import UIKit

class BugViewController: UIViewController

    func perform(operation: (Double) -> Double) 
    

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

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

使用 Objective-C 选择器 'perform:' 的方法 'perform' 与之前使用相同 Objective-C 选择器的声明冲突

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

import UIKit

class BugViewController

    func perform(operation: (Double) -> Double) 
    

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

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

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

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

【问题讨论】:

您可以在 Yosemite 上运行 Xcode 6.2。您可以从应用商店下载它,它可以在您的系统上使用 Beta 版本。目前我不建议在斯坦福课程中使用 Xcode 6.3,因为它是 beta 版,并且包含 Swift 1.2,这与视频中使用的早期版本的 Swift 不同。 4 月 5 日用户 (feb) 的(当前接受的)答案不再是最好的答案。相反,4 月 16 日 (James Zhang) 的回答更加具体和正确。 【参考方案1】:

我自己也在上斯坦福的课程,我也在这里卡了很长时间,但经过一番搜索,我从这里找到了一些东西:Xcode release notes,并在下面提到了一些东西:

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)

SDK 中的符号在使用 Open Quickly 时不可用 使用 Swift 的项目或工作区。 (20349540)

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

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

【讨论】:

这个解决方案是我发现恕我直言的最可行的解决方案,因为将此方法设置为私有完全有意义 请注意,现在还有一个@nonobjc 属性,可用于从Objective-C 运行时中排除方法。 下面是@ErikJ 的评论和polarwar 的回答。这似乎是 Swift 2 和 xcode 7 的最佳答案。如果您还没有更新,我强烈推荐它。 @polarwar 下面的答案是 Swift 2 的最佳答案:***.com/a/31500740/144088【参考方案2】:

Objective-C 不支持方法重载,你必须使用不同的方法名。当您继承 UIViewController 时,您继承了 NSObject 并使该类可与 Obj-C 互操作。另一方面,Swift 确实支持重载,这就是它在删除继承时起作用的原因。

【讨论】:

Objective-C SUPPORTS 方法覆盖(带有(可抑制的)编译器警告通知您重载已实现的内容),Apple 只是不希望您这样做以使他们的框架远离超载。我正在使用这样的重载 f.e.每天在UIFont 上。 @polarwar 下面的答案是 Swift 2 的最佳答案:***.com/a/31500740/144088【参考方案3】:

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

func methodOne(par1, par2) ...

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

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

func methodOne() ...

@nonobjc
func methodOne() ...

【讨论】:

这解决了 swift 2(及更高版本)的问题。应该更新为最正确的答案。泰。 对于使用 Swift 2 和 Xcode 7 + 的任何人,这是我同意 polarwar 的正确答案【参考方案4】:

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

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

要解决此问题,请使用不同的名称:例如 func perform1(operation: (Double) -> Double)func perform2(operation: (Double, Double) -> Double)


我认为处理此问题的最佳方法是为您的 perform() 方法提供更具描述性的名称。这些方法有什么作用?他们如何改变视图控制器的状态?查看其他UIViewController 方法以了解方法命名的风格,或阅读Method Names Should Be Expressive and Unique Within a Class

【讨论】:

谢谢 - 这完美地回答了我的问题,因为你是第一个我会将此标记为正确的。 话虽如此,我仍然不明白为什么讲座上的代码可以工作,因为我很确定它做了我的非编译代码所做的事情!嘿嗬 - 我会回去仔细检查。一定有什么不同。 @Auspice 他们用于视频的 Xcode 版本可能没有产生错误,但这仍然是一个问题。直到 Xcode 6.3 编译器才能够检测到这一点并警告您。 Paul Hegarty 想在这里演示函数“重载”(两个函数名称相同,但参数集不同),所以他确实故意使用相同的方法名称!仅在 Swift 中允许重载,在 Objective-C 中不允许。这就是为什么解决方案要么删除 UIViewController 的继承形式(它是一个 Objective-C 类),要么将方法声明为私有。这两种解决方案都在此处的其他答案中进行了详细说明。 实际上我在函数前面使用了私有关键字。 like,private func performOperation(operation: Double -> Double) 和 private func performOperation(operation: (Double,Double) -> Double) 这里我借助 PRIVATE 实现了方法重载。因为我只在 ViewController.Swift 中使用了它们。为什么编译器没有说任何错误?【参考方案5】:

来自“Xcode 6.3 发行说明”下的https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html ->“Swift 语言更改”您可以找到

Swift 现在可以检测 Swift 类型系统中的重载和覆盖与通过 Objective-C 运行时看到的有效行为之间的差异。

【讨论】:

【参考方案6】:

由于有两个具有相同 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 

【讨论】:

以上是关于编译器错误:使用 Objective-C 选择器的方法与先前使用相同 Objective-C 选择器的声明冲突的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 带有Objective-C选择器'*'的方法'*()'与具有相同Objective-C选择器的超类'UIView'中的'*'的getter冲突

快速更改选择器的字体及其大小

使用支持 Objective-C 的 gcc 错误编译 Android NDK

在使用 MagicalRecord CocoaPod 初始化 +initialize 时,Objective-C 无法识别的选择器

使用 LLVM ExecutionEngine 调用 Objective-C 方法时,所有选择器都无法识别

boost::spirit 解析器的编译错误