Erreur du compilateur: la méthode avec le sélecteur Objective-C entre en conflit avec la déclaration précédente avec le même sélecteur Objective-C

209

Je commence à apprendre Swift et j'ai suivi les très bonnes conférences vidéo de l'Université de Stanford sur YouTube. Voici un lien si vous êtes intéressé ou si cela aide (bien qu'il ne soit pas nécessaire de comprendre mon problème):

Développer des applications iOS 8 avec Swift - 2. Plus Xcode et Swift, MVC

En suivant les cours, je suis arrivé à un point où (pour autant que je sache) mon code était identique au code de la vidéo mais sur mon système, j'ai eu une erreur de compilation. Après beaucoup d'essais et d'erreurs, j'ai réussi à réduire mon code à deux exemples, dont l'un génère une erreur, l'autre ou qui ne le fait pas, mais je n'ai aucune idée de ce qui cause réellement l'erreur ou comment la résoudre.

Le code qui crée l'erreur est:

import UIKit

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

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

Cela crée l'erreur de compilation suivante:

La méthode 'perform' avec le sélecteur Objective-C 'perform:' entre en conflit avec la déclaration précédente avec le même sélecteur Objective-C

En supprimant simplement la sous-classification de UIViewController, le code compile:

import UIKit

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

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

Quelques autres informations qui peuvent être pertinentes ou non:

  • J'ai récemment mis à niveau vers Yosemite.
  • Lorsque j'ai installé Xcode, je me suis retrouvé avec une version Beta (version 6.3 (6D543q)) parce que (si je me souviens bien), c'était la version dont j'avais besoin pour fonctionner sur ma version d'OS X.

J'espère à moitié que c'est un bogue dans le compilateur car sinon cela n'a aucun sens pour moi. Toute aide reçue avec reconnaissance!

Auspice
la source
3
Vous pouvez exécuter Xcode 6.2 sur Yosemite. Vous pouvez le télécharger sur l'App Store et il peut vivre sur votre système avec la version Beta. Je ne recommanderais pas d'utiliser Xcode 6.3 pour la classe Stanford à ce stade, car il est en version bêta et inclut Swift 1.2, qui est différent de la version antérieure de Swift utilisée dans les vidéos.
vacawama
2
La réponse (actuellement acceptée) de l'utilisateur (fév) du 5 avril n'est plus la meilleure. Au lieu de cela, la réponse de (James Zhang) du 16 avril est plus précise et correcte.
phlebotinum

Réponses:

144

Objective-C ne prend pas en charge la surcharge de méthode, vous devez utiliser un nom de méthode différent. Lorsque vous avez hérité d'UIViewController, vous avez hérité de NSObject et rendu la classe interopérable avec Obj-C. Swift, en revanche, prend en charge la surcharge, c'est pourquoi cela fonctionne lorsque vous supprimez l'héritage.

fév
la source
2
La méthode Objective-C SUPPORTS remplace (avec un avertissement de compilateur (supprimable) vous informant de la surcharge de quelque chose déjà implémenté), Apple ne veut tout simplement pas que vous le fassiez pour éviter que ses frameworks ne soient surchargés. J'utilise de telles surcharges par exemple UIFonttous les jours.
Michi
La réponse de @ polarwar ci-dessous est la meilleure pour Swift 2: stackoverflow.com/a/31500740/144088
Crashalot
237

Je prends moi-même également le cours Standford et je suis resté coincé ici pendant longtemps aussi, mais après quelques recherches, j'ai trouvé quelque chose d'ici: Notes de publication Xcode et il a mentionné quelque chose ci-dessous:

Swift 1.2 est strict quant à la vérification de la surcharge basée sur le type des méthodes et des initialiseurs @objc, ce qui n'est pas pris en charge par 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))
}

Ce code fonctionnerait lorsqu'il serait appelé depuis Swift, mais pourrait facilement planter s'il était appelé depuis Objective-C. Pour résoudre ce problème, utilisez un type qui n'est pas pris en charge par Objective-C pour empêcher le compilateur Swift d'exposer le membre au runtime Objective-C:

  • Si cela a du sens, marquez le membre comme privé pour désactiver l'inférence de @objc.
  • Sinon, utilisez un paramètre factice avec une valeur par défaut, par exemple: _ nonobjc: () = (). (19826275)

Les substitutions de méthodes exposées à Objective-C dans des sous-classes privées ne sont pas supposées être @objc, provoquant le plantage du compilateur Swift. Ajoutez explicitement l'attribut @objc à ces méthodes de substitution. (19935352)

Les symboles des SDK ne sont pas disponibles lorsque vous utilisez Ouvrir rapidement dans un projet ou un espace de travail qui utilise Swift. (20349540)

ce que j'ai fait, c'était simplement d'ajouter "privé" devant la méthode de remplacement comme ceci:

    private func performOperation(operation: Double -> Double) {
    if operandStack.count >= 1 {
        displayValue = operation(operandStack.removeLast())
        enter()
    }
}
James Zhang
la source
3
Cette solution est la plus viable que je trouve à mon humble avis, car cela a tout à fait du sens de mettre cette méthode en privé
démentielle
38
Veuillez noter qu'il existe désormais également un attribut @nonobjc, qui peut être utilisé pour exclure une méthode du runtime Objective-C.
Erik J
2
J'appuie le commentaire de @ ErikJ et la réponse de polarwar ci-dessous. Cela semble être la meilleure réponse pour aller de l'avant avec Swift 2 et xcode 7. Si vous n'avez pas encore mis à jour, je le recommande fortement.
Austin A
La réponse de @ polarwar ci-dessous est la meilleure pour Swift 2: stackoverflow.com/a/31500740/144088
Crashalot
111

Comme il a déjà été répondu, ObjC ne prend pas en charge la surcharge de méthode (deux méthodes avec le même nom) et dans swift 2 sous Xcode 7, il existe deux options pour résoudre ce type de problèmes. Une option consiste à renommer la méthode à l'aide de l'attribut:@objc(newNameMethod:)

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

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

une autre option pour résoudre ce problème dans Xcode 7+ consiste à appliquer un @nonobjcattribut à n'importe quelle méthode, indice ou initialiseur

func methodOne() {...}

@nonobjc
func methodOne() {...}
polarware
la source
6
cela résout le problème pour swift 2 (et plus). devrait être mis à jour comme réponse la plus correcte. ty.
Maxim Veksler du
2
Pour toute personne utilisant Swift 2 et Xcode 7 +, c'est la bonne réponse, je suis d'accord avec polarwar
TerNovi
17

Le problème est UIViewControllerune @objcclasse. Lors de l'héritage de UIViewController, BugViewControllerest également une @objcclasse.

Cela signifie qu'elle doit être conforme aux règles des sélecteurs Objective-C (le nom d'une méthode). Les méthodes func perform(operation: (Double) -> Double)et les func perform(operation: (Double, Double) -> Double)deux ont le même sélecteur @selector(perform:). Ce n'est pas permis.

Pour résoudre ce problème, utilisez des noms différents: comme func perform1(operation: (Double) -> Double)et func perform2(operation: (Double, Double) -> Double).


Je pense que la meilleure façon de gérer cela est de donner à vos perform()méthodes des noms plus descriptifs. Que font ces méthodes? Comment changent-ils l'état du contrôleur de vue? Regardez les autres UIViewControllerméthodes pour avoir une idée du style de dénomination des méthodes, ou lisez les noms de méthode doivent être expressifs et uniques dans une classe

Jeffery Thomas
la source
Merci - cela répond parfaitement à ma question et comme vous étiez le premier, je marquerai cela comme correct.
Auspice
Cela dit, je ne comprends toujours pas pourquoi le code de la conférence a fonctionné, car je suis sûr qu'il a fait ce que mon code non compilateur a fait! Hé ho - je vais revenir en arrière et revérifier. Il doit y avoir quelque chose de différent.
Auspice
2
@Auspice Cela n'a peut-être pas produit d'erreurs avec la version de Xcode qu'ils utilisaient pour les vidéos, mais c'était toujours un problème. Ce n'est qu'après Xcode 6.3 que le compilateur a pu détecter cela et vous avertir.
Mick MacCallum
3
Paul Hegarty veut démontrer ici la «surcharge» de la fonction (2 fonctions avec le même nom, mais un ensemble d'arguments différent), il utilise donc exprès le même nom de méthode! La surcharge n'est autorisée que dans Swift, pas dans Objective-C. C'est pourquoi la solution consiste soit à supprimer le formulaire d'héritage UIViewController (qui est une classe Objective-C), soit à déclarer la méthode privée. Les deux solutions sont expliquées en détail dans les autres réponses ici.
Ronny Webers
En fait, j'ai utilisé un mot-clé privé en face de la fonction. comme private func performOperation (opération: Double -> Double) {} et private func performOperation (opération: (Double, Double) -> Double) {} Ici, j'ai réalisé la surcharge de méthode avec l'aide de PRIVATE. car j'ai utilisé les deux dans ViewController.Swift uniquement. Pourquoi le compilateur ne dit aucune erreur?
iTag
2

J'ai eu la même erreur car j'avais deux méthodes avec la même signature Obj-C:

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

Je ne voulais pas marquer l'un d'eux comme @nonobjc en raison de la possibilité de conséquences imprévues lors de l'exécution. (Quelqu'un peut me corriger s'il n'y a pas de possibilité)

Résolu en utilisant la fonction de nom de paramètre externe de Swift (j'ai fait le même nom externe que le nom local) à la deuxième méthode, qui change efficacement la signature de la méthode Obj-c:

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