Compilerfehler: Methode mit Objective-C-Selektor widerspricht der vorherigen Deklaration mit demselben Objective-C-Selektor

209

Ich fange an, Swift zu lernen und habe die sehr guten Videovorträge der Stanford University auf YouTube verfolgt. Hier ist ein Link, wenn Sie interessiert sind oder es hilft (obwohl es nicht erforderlich ist, mein Problem zu verstehen):

Entwickeln von iOS 8-Apps mit Swift - 2. Mehr Xcode und Swift, MVC

Während ich den Vorlesungen folgte, kam ich zu einem Punkt, an dem (soweit ich das beurteilen konnte) mein Code mit dem Code im Video identisch war, aber auf meinem System ein Compilerfehler auftrat. Nach vielen Versuchen und Irrtümern habe ich es geschafft, meinen Code auf zwei Beispiele zu reduzieren, von denen eines einen Fehler erzeugt, das andere oder nicht, aber ich habe keine Ahnung, was den Fehler tatsächlich verursacht oder wie er behoben werden kann.

Der Code, der den Fehler verursacht, lautet:

import UIKit

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

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

Dies erzeugt den folgenden Compilerfehler:

Methode 'perform' mit Objective-C-Selektor 'perform:' widerspricht der vorherigen Deklaration mit demselben Objective-C-Selektor

Durch einfaches Entfernen der Unterklasse von UIViewController wird der Code kompiliert:

import UIKit

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

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

Einige andere Informationen, die relevant sein können oder nicht:

  • Ich habe kürzlich ein Upgrade auf Yosemite durchgeführt.
  • Als ich Xcode installiert habe, habe ich eine Beta-Version (Version 6.3 (6D543q)) erhalten, da dies (wenn ich mich richtig erinnere) die Version war, die ich für meine Version von OS X benötigte.

Ich hoffe halb, dass dies ein Fehler im Compiler ist, da dies sonst für mich keinen Sinn ergibt. Jede Hilfe sehr dankbar erhalten!

Auspice
quelle
3
Sie können Xcode 6.2 auf Yosemite ausführen. Sie können es aus dem App Store herunterladen und es kann mit der Beta-Version auf Ihrem System ausgeführt werden. Ich würde an dieser Stelle nicht empfehlen, Xcode 6.3 für die Stanford-Klasse zu verwenden, da es Beta-Version ist und Swift 1.2 enthält, das sich von der früheren Version von Swift unterscheidet, die in den Videos verwendet wurde.
Vacawama
2
Die (derzeit akzeptierte) Antwort des Benutzers (Februar) vom 5. April ist nicht mehr die beste. Stattdessen ist die Antwort von (James Zhang) vom 16. April spezifischer und korrekter.
Phlebotinum

Antworten:

144

Objective-C unterstützt keine Methodenüberladung. Sie müssen einen anderen Methodennamen verwenden. Wenn Sie UIViewController geerbt haben, haben Sie NSObject geerbt und die Klasse für Obj-C interopierbar gemacht. Swift hingegen unterstützt das Überladen. Deshalb funktioniert es, wenn Sie die Vererbung entfernen.

feb
quelle
2
Die Objective-C SUPPORTS-Methode überschreibt (mit einer Reihe von (unterdrückbaren) Compiler-Warnungen, die Sie über das Überladen von bereits implementierten Elementen informieren). Apple möchte nur nicht, dass Sie dies tun, um zu verhindern, dass ihre Frameworks überladen werden. Ich benutze solche Überlastungen zB UIFontjeden Tag.
Michi
@ Polarwars Antwort unten ist die beste für Swift 2: stackoverflow.com/a/31500740/144088
Crashalot
237

Ich selbst nehme auch am Standford-Kurs teil und bin auch schon lange hier festgefahren, aber nach einigem Suchen habe ich etwas von hier gefunden: Xcode-Versionshinweise und es wurde unten etwas erwähnt:

In Swift 1.2 wird strengstens die typbasierte Überladung von @ objc-Methoden und -Initialisierern überprüft, was von Objective-C nicht unterstützt wird.

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

Dieser Code würde funktionieren, wenn er von Swift aufgerufen wird, könnte jedoch leicht abstürzen, wenn er von Objective-C aufgerufen wird. Verwenden Sie zur Lösung dieses Problems einen Typ, der von Objective-C nicht unterstützt wird, um zu verhindern, dass der Swift-Compiler das Mitglied der Objective-C-Laufzeit aussetzt:

  • Wenn es sinnvoll ist, markieren Sie das Mitglied als privat, um die Inferenz von @objc zu deaktivieren.
  • Verwenden Sie andernfalls einen Dummy-Parameter mit einem Standardwert, zum Beispiel: _ nonobjc: () = (). (19826275)

Überschreibungen von Methoden, die Objective-C in privaten Unterklassen ausgesetzt sind, werden nicht als @objc bezeichnet, was zum Absturz des Swift-Compilers führt. Fügen Sie das Attribut @objc explizit zu solchen überschreibenden Methoden hinzu. (19935352)

Symbole aus SDKs sind nicht verfügbar, wenn Sie Schnell öffnen in einem Projekt oder Arbeitsbereich verwenden, der Swift verwendet. (20349540)

Was ich getan habe, war nur "privat" vor der Override-Methode wie folgt hinzuzufügen:

    private func performOperation(operation: Double -> Double) {
    if operandStack.count >= 1 {
        displayValue = operation(operandStack.removeLast())
        enter()
    }
}
James Zhang
quelle
3
Diese Lösung ist die praktikabelste, die ich imho finde, da es absolut sinnvoll ist, diese Methode privat zu setzen
demental
38
Bitte beachten Sie, dass es jetzt auch ein @ nonobjc-Attribut gibt, mit dem eine Methode von der Objective-C-Laufzeit ausgeschlossen werden kann.
Erik J
2
Ich stimme dem Kommentar von @ ErikJ und der Antwort von Polarwar unten zu. Dies scheint die beste Antwort für Swift 2 und xcode 7 zu sein. Wenn Sie noch kein Update durchgeführt haben, empfehle ich es dringend.
Austin A
@ Polarwars Antwort unten ist die beste für Swift 2: stackoverflow.com/a/31500740/144088
Crashalot
111

Wie bereits beantwortet, unterstützt ObjC das Überladen von Methoden nicht (zwei Methoden mit demselben Namen). In Swift 2 unter Xcode 7 gibt es zwei Möglichkeiten, um diese Art von Problemen zu lösen. Eine Möglichkeit besteht darin, die Methode mithilfe des Attributs umzubenennen:@objc(newNameMethod:)

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

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

Eine weitere Möglichkeit, dieses Problem in Xcode 7+ zu lösen, besteht darin, @nonobjcAttribute auf eine beliebige Methode, einen Index oder einen Initialisierer anzuwenden

func methodOne() {...}

@nonobjc
func methodOne() {...}
Polarware
quelle
6
Dies löst das Problem für Swift 2 (und höher). sollte als richtigste Antwort aktualisiert werden. ty.
Maxim Veksler
2
Für alle, die Swift 2 und Xcode 7 + verwenden, ist dies die richtige Antwort. Ich stimme polarwar zu
TerNovi
17

Das Problem UIViewControllerist eine @objcKlasse. Wenn von vererben UIViewController, BugViewControllerist auch eine @objcKlasse.

Dies bedeutet, dass es den Regeln der Objective-C-Selektoren (dem Namen einer Methode) entsprechen muss. Die Methoden func perform(operation: (Double) -> Double)und func perform(operation: (Double, Double) -> Double)beide haben den gleichen Selektor @selector(perform:). Das ist nicht erlaubt.

Um dies zu beheben, verwenden Sie verschiedene Namen: like func perform1(operation: (Double) -> Double)und func perform2(operation: (Double, Double) -> Double).


Ich denke, der beste Weg, damit umzugehen, besteht darin, Ihren perform()Methoden aussagekräftigere Namen zu geben . Was machen diese Methoden? Wie ändern sie den Status des View Controllers? Schauen Sie sich die anderen UIViewControllerMethoden an, um ein Gefühl für den Stil der Methodenbenennung zu bekommen, oder lesen Sie Methodennamen sollten innerhalb einer Klasse ausdrucksstark und eindeutig sein

Jeffery Thomas
quelle
Danke - das beantwortet meine Frage perfekt und da Sie der Erste waren, werde ich dies als richtig markieren.
Auspice
Trotzdem verstehe ich immer noch nicht, warum der Code in der Vorlesung funktioniert hat, da ich ziemlich sicher bin, dass er das getan hat, was mein nicht kompilierter Code getan hat! Hey ho - ich werde zurückgehen und es noch einmal überprüfen. Es muss etwas anderes geben.
Auspice
2
@Auspice Es hat möglicherweise keine Fehler mit der Version von Xcode verursacht, die sie für die Videos verwendet haben, aber es war immer noch ein Problem. Erst mit Xcode 6.3 konnte der Compiler dies erkennen und Sie warnen.
Mick MacCallum
3
Paul Hegarty möchte hier die Funktion 'Überladung' demonstrieren (2 Funktionen mit demselben Namen, aber unterschiedlichen Argumenten), also verwendet er absichtlich denselben Methodennamen! Überladen ist nur in Swift zulässig, nicht in Objective-C. Aus diesem Grund besteht die Lösung darin, entweder die Vererbung von UIViewController (einer Objective-C-Klasse) zu entfernen oder die Methode als privat zu deklarieren. Beide Lösungen werden in den anderen Antworten hier ausführlich erläutert.
Ronny Webers
Eigentlich habe ich vor der Funktion ein privates Schlüsselwort verwendet. wie private func performOperation (Operation: Double -> Double) {} und private func performOperation (Operation: (Double, Double) -> Double) {} Hier habe ich mit Hilfe von PRIVATE die Methodenüberladung erreicht. weil ich beide nur in ViewController.Swift verwendet habe. Warum sagt der Compiler keinen Fehler?
iTag
2

Ich habe den gleichen Fehler erhalten, weil ich zwei Methoden mit derselben Obj-C-Signatur habe:

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

Ich wollte einen von ihnen nicht als @nonobjc markieren, da zur Laufzeit möglicherweise unvorhergesehene Konsequenzen auftreten. (Jemand kann mich korrigieren, wenn es keine Möglichkeit gibt)

Es wurde behoben, indem die Funktion für externe Parameternamen von Swift (ich habe den externen Namen mit dem lokalen Namen identisch gemacht) für die zweite Methode verwendet wurde, wodurch die Signatur der Obj-c-Methode effektiv geändert wird:

static func prepareForUpSyncing(objs objs : [NSManagedObject]!) -> Bool {
Protongun
quelle