コンパイラエラー:Objective-Cセレクターを持つメソッドは、同じObjective-Cセレクターによる以前の宣言と競合します


209

私はSwiftを学び始めており、YouTubeでスタンフォード大学の非常に優れたビデオ講義を続けています。興味があるか、役立つ場合のリンクは次のとおりです(ただし、私の問題を理解する必要はありません)。

Swiftを使用したiOS 8アプリの開発-2. XcodeとSwift、MVCの追加

講義を続けている間に、私のコードがビデオ内のコードと同じであるという点に達しましたが、私のシステムではコンパイラエラーが発生しました。多くの試行錯誤の結果、コードを2つの例に減らすことができました。1つはエラーを生成するもの、もう1つは生成しないものですが、実際にエラーを引き起こしている原因や解決方法はわかりません。

エラーを作成するコードは次のとおりです。

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) {
    }
}

関連するかもしれないし、関連しないかもしれないいくつかの他の情報:

  • 最近ヨセミテにアップグレードしました。
  • Xcodeをインストールすると、ベータ版(バージョン6.3(6D543q))になりました(私が正しく覚えていれば)これが私のバージョンのOS Xで実行する必要があったためです。

これがコンパイラのバグであることを期待しています。それ以外の場合、これは私には意味がありません。助けてくれてとても感謝しています!


3
YosemiteでXcode 6.2を実行できます。アプリストアからダウンロードでき、ベータ版を使用してシステムにインストールできます。現時点では、スタンフォードクラスにXcode 6.3を使用することはお勧めしません。Xcode6.3はベータ版であり、ビデオで使用されていた以前のバージョンのSwiftとは異なるSwift 1.2が含まれているためです。
vacawama 2015

2
4月5日のユーザー(2月)からの(現在受け入れられている)回答は、もはや最良のものではありません。代わりに、4月16日の(James Zhang)からの回答はより具体的で正確です。
phlebotinum 2015年

回答:


144

Objective-Cはメソッドのオーバーロードをサポートしていません。別のメソッド名を使用する必要があります。UIViewControllerを継承すると、NSObjectを継承し、クラスをObj-Cと相互運用できるようにします。一方、Swiftはオーバーロードをサポートしているため、継承を削除すると機能します。


2
Objective-C SUPPORTSメソッドのオーバーライド(既に実装されているもののオーバーロードについて通知する(抑制可能な)コンパイラ警告のコンボ)で、Appleはフレームワークがオーバーロードされないようにするためにそうすることを望まないだけです。私はUIFont毎日そのようなオーバーロードfeを使用しています。
Michi

以下@ polarwarの答えはスウィフト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を使用するプロジェクトまたはワークスペースでOpen Quicklyを使用する場合、SDKのシンボルは使用できません。(20349540)

私がしたことは、このようなオーバーライドメソッドの前に「プライベート」を追加することでした:

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

3
これは完全にこのメソッドはプライベートに設定することは理にかなっていて、このソリューションは、最も実行可能なIの検索私見です
demental

38
Objective-Cランタイムからメソッドを除外するために使用できる@nonobjc属性もあることに注意してください。
Erik J

2
以下に@ErikJのコメントとpolarwarの回答を2番目に掲載します。これは、Swift 2とxcode 7で前進する最良の答えのようです。まだ更新していない場合は、強くお勧めします。
オースティンA

以下@ polarwarの答えはスウィフト2のために最良のものである:stackoverflow.com/a/31500740/144088
Crashalot

111

すでに回答されているように、ObjCはメソッドのオーバーロード(同じ名前の2つのメソッド)をサポートしていません。Xcode7のswift 2では、この種の問題を解決する2つのオプションがあります。1つのオプションは、属性を使用してメソッドの名前を変更することです。@objc(newNameMethod:)

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

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

Xcode 7以降でこの問題を解決する別のオプションは、@nonobjcメソッド、添え字、または初期化子に属性を適用すること です

func methodOne() {...}

@nonobjc
func methodOne() {...}

6
これにより、swift 2(以降)の問題が解決されます。最も正しい答えとして更新する必要があります。ty。
Maxim Veksler、2015

2
Swift 2とXcode 7を使用している人にとっては、これが正解です。私はpolarwarに同意します
TerNovi

17

ある問題が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メソッドを調べて、メソッドの命名スタイルの感触をつかむか、メソッド名はクラス内で表現可能で一意でなければならないを読んでください。


おかげで-これは私の質問に完全に答えます。あなたが最初だったので、これを正しいものとしてマークします。
Auspice

講義のコードが機能していない理由はまだわかりませんが、それが私の非コンパイルコードが行ったことをかなり確信しています!こんにちは-私は戻ってそれを再確認します。何か違うものがあるに違いない。
Auspice

2
@Auspiceビデオに使用していたXcodeのバージョンでエラーが発生しなかった可能性がありますが、それでも問題がありました。コンパイラがこれを検出して警告を表示できるようになったのは、Xcode 6.3まででした。
Mick MacCallum、2015

3
Paul Hegartyはここで関数のオーバーロード(同じ名前の2つの関数で引数のセットが異なる)を示したいので、意図的に同じメソッド名を使用します!オーバーロードはSwiftでのみ許可され、Objective-Cでは許可されません。そのため、ソリューションはUIViewController(Objective-Cクラス)の継承フォームを削除するか、メソッドをプライベートとして宣言します。両方のソリューションについては、こちらの他の回答で詳しく説明しています。
ロニーウェバーズ

実際、私は関数の前でプライベートキーワードを使用しました。like、private func performOperation(operation:Double-> Double){}およびprivate func performOperation(operation:(Double、Double)-> Double){}ここで、PRIVATEの助けを借りてメソッドのオーバーロードを実現しました。両方ともViewController.Swiftでのみ使用したためです。コンパイラがエラーを表示しないのはなぜですか?
iTag 2015


2

同じObj-Cシグネチャを持つ2つのメソッドがあるため、同じエラーが発生しました。

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

実行時に予期しない結果が発生する可能性があるため、@ nonobjcとしてマークしたくありませんでした。(可能性がなければ誰かが私を修正することができます)

Swiftの外部パラメーター名機能(外部名をローカル名と同じにした)を使用して2番目のメソッドに解決し、Obj-cメソッドシグネチャを効果的に変更します。

static func prepareForUpSyncing(objs objs : [NSManagedObject]!) -> Bool {
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.