Errore del compilatore: il metodo con il selettore Objective-C è in conflitto con la precedente dichiarazione con lo stesso selettore Objective-C


209

Sto iniziando a imparare Swift e ho seguito le ottime lezioni video della Stanford University su YouTube. Ecco un link se sei interessato o aiuta (anche se non è necessario per capire il mio problema):

Sviluppo di app iOS 8 con Swift - 2. Più Xcode e Swift, MVC

Mentre seguivo le lezioni sono arrivato a un punto in cui (per quanto potevo dire) il mio codice era identico al codice nel video ma sul mio sistema ho riscontrato un errore del compilatore. Dopo molte prove ed errori sono riuscito a ridurre il mio codice a due esempi, uno dei quali genera un errore, l'altro o che non lo fa, ma non ho idea di cosa stia effettivamente causando l'errore o come risolverlo.

Il codice che crea l'errore è:

import UIKit

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

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

Questo crea il seguente errore del compilatore:

Il metodo 'perform' con il selettore Objective-C 'perform:' è in conflitto con la precedente dichiarazione con lo stesso selettore Objective-C

Semplicemente rimuovendo la sottoclasse di UIViewController il codice si compila:

import UIKit

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

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

Alcune altre informazioni che potrebbero essere o non essere rilevanti:

  • Ho recentemente aggiornato a Yosemite.
  • Quando ho installato Xcode, ho finito con una versione Beta (Versione 6.3 (6D543q)) perché (se ricordo bene) questa era la versione che dovevo eseguire sulla mia versione di OS X.

Spero per metà che questo sia un bug nel compilatore perché altrimenti non ha alcun senso per me. Qualsiasi aiuto ricevuto con gratitudine!


3
Puoi eseguire Xcode 6.2 su Yosemite. Puoi scaricarlo dall'App Store e può vivere sul tuo sistema con la versione Beta. Non consiglierei di usare Xcode 6.3 per la classe Stanford a questo punto perché è beta e include Swift 1.2 che è diverso rispetto alla versione precedente di Swift usata nei video.
vacawama,

2
La risposta (attualmente accettata) dell'utente (feb) del 5 aprile non è più la migliore. Invece la risposta di (James Zhang) del 16 aprile è più specifica e corretta.
flebotino,

Risposte:


144

Objective-C non supporta il sovraccarico del metodo, è necessario utilizzare un nome di metodo diverso. Quando hai ereditato UIViewController hai ereditato NSObject e reso la classe interoperabile su Obj-C. Swift d'altra parte supporta il sovraccarico, ecco perché funziona quando si rimuove l'eredità.


2
Metodo SUPPORTI Objective-C che ha la precedenza (con una truffa di avvisi del compilatore (sopprimibili) che ti avvisano di sovraccaricare qualcosa di già implementato), Apple non vuole che tu lo faccia per evitare che i loro framework siano sovraccarichi. Sto usando questi sovraccarichi UIFontogni giorno.
Michi,

@ risposta di polarwar sotto è la migliore per Swift 2: stackoverflow.com/a/31500740/144088
Crashalot

237

Anch'io sto prendendo il corso di Standford e sono rimasto bloccato qui per molto tempo, ma dopo alcune ricerche, ho trovato qualcosa da qui: note di rilascio di Xcode e menzionato qualcosa di seguito:

Swift 1.2 è severo nel controllare il sovraccarico basato su tipi di metodi e inizializzatori @objc, qualcosa che non è supportato da 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))
}

Questo codice funzionerebbe quando invocato da Swift, ma potrebbe bloccarsi facilmente se invocato da Objective-C. Per risolvere questo problema, utilizzare un tipo non supportato da Objective-C per impedire al compilatore Swift di esporre il membro al runtime Objective-C:

  • Se ha senso, contrassegnare il membro come privato per disabilitare l'inferenza di @objc.
  • In caso contrario, utilizzare un parametro fittizio con un valore predefinito, ad esempio: _ nonobjc: () = (). (19826275)

Le sostituzioni di metodi esposti a Objective-C in sottoclassi private non sono inferite a @objc, causando il crash del compilatore Swift. Aggiungi esplicitamente l'attributo @objc a tali metodi di sostituzione. (19935352)

I simboli degli SDK non sono disponibili quando si utilizza Apri rapidamente in un progetto o un'area di lavoro che utilizza Swift. (20349540)

quello che ho fatto è stato semplicemente aggiungere "privato" di fronte al metodo di sostituzione come questo:

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

3
Questa soluzione è la più praticabile che trovo imho, poiché ha
perfettamente

38
Si noti che ora esiste anche un attributo @nonobjc, che può essere utilizzato per escludere un metodo dal runtime di Objective-C.
Erik J,

2
Secondo il commento di @ErikJ e la risposta di polarwar di seguito. Questa sembra essere la migliore risposta andando avanti con Swift 2 e xcode 7. Se non l'hai ancora aggiornato, lo consiglio vivamente.
Austin,

@ risposta di polarwar sotto è la migliore per Swift 2: stackoverflow.com/a/31500740/144088
Crashalot

111

Come è già stato risposto, ObjC non supporta il sovraccarico dei metodi (due metodi con lo stesso nome) e in swift 2 sotto Xcode 7 ci sono due opzioni per risolvere questo tipo di problemi. Un'opzione è quella di rinominare il metodo usando l'attributo:@objc(newNameMethod:)

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

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

un'altra opzione per risolvere questo problema in Xcode 7+ è applicando l' @nonobjcattributo a qualsiasi metodo, pedice o inizializzatore

func methodOne() {...}

@nonobjc
func methodOne() {...}

6
questo risolve il problema per swift 2 (e up). dovrebbe essere aggiornato come la risposta più corretta. ty.
Maxim Veksler,

2
Per chiunque usi Swift 2 e Xcode 7 + questa è la risposta corretta Sono d'accordo con polarwar
TerNovi

17

Il problema UIViewControllerè che una @objcclasse. Quando eredita da UIViewController, BugViewControllerè anche una @objcclasse.

Ciò significa che deve essere conforme alle regole dei selettori Objective-C (il nome di un metodo). I metodi func perform(operation: (Double) -> Double)ed func perform(operation: (Double, Double) -> Double)entrambi hanno lo stesso selettore @selector(perform:). Questo non è permesso

Per risolvere questo problema, usa nomi diversi: like func perform1(operation: (Double) -> Double)e func perform2(operation: (Double, Double) -> Double).


Penso che il modo migliore per gestirlo sia quello di dare ai tuoi perform()metodi nomi più descrittivi. Cosa fanno questi metodi? Come cambiano lo stato del controller di visualizzazione? Guarda gli altri UIViewControllermetodi per avere un'idea dello stile di denominazione dei metodi, oppure leggi I nomi dei metodi devono essere espressivi e unici all'interno di una classe


Grazie - questo risponde perfettamente alla mia domanda e, dato che eri il primo, lo segnerò come corretto.
Auspice,

Detto questo, non capisco ancora perché il codice della lezione abbia funzionato, dato che sono abbastanza sicuro che ha fatto quello che ha fatto il mio codice di non compilazione! Ehi ho - Tornerò e ricontrollerò. Ci deve essere qualcosa di diverso.
Auspice,

2
@Auspice Potrebbe non aver prodotto errori con la versione di Xcode che stavano usando per i video ma era ancora un problema. Non è stato fino a Xcode 6.3 che il compilatore è stato in grado di rilevare questo e avvisarti.
Mick MacCallum

3
Paul Hegarty vuole dimostrare la funzione di "sovraccarico" qui (2 funzioni con lo stesso nome, ma diverso set di argomenti), quindi usa apposta lo stesso nome di metodo! Il sovraccarico è consentito solo in Swift, non in Objective-C. Ecco perché la soluzione è rimuovere il modulo di ereditarietà UIViewController (che è una classe Objective-C) o dichiarare il metodo privato. Entrambe le soluzioni sono spiegate in dettaglio nelle altre risposte qui.
Ronny Webers,

In realtà ho usato una parola chiave privata davanti alla funzione. come, private func performOperation (operazione: Double -> Double) {} e private func performOperation (operazione: (Double, Double) -> Double) {} Qui ho ottenuto il sovraccarico del metodo con l'aiuto di PRIVATE. perché li ho usati entrambi in ViewController.Swift. Perché il compilatore non dice alcun errore?
iTag il


2

Ho avuto lo stesso errore a causa della presenza di due metodi con la stessa firma Obj-C:

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

Non volevo contrassegnarne uno come @nonobjc a causa della possibilità di conseguenze indesiderate in fase di esecuzione. (Qualcuno può correggermi se non c'è possibilità)

Risolto usando la funzione nome parametro esterno di Swift (ho reso il nome esterno uguale al nome locale) al secondo metodo, che modifica efficacemente la firma del metodo Obj-c:

static func prepareForUpSyncing(objs objs : [NSManagedObject]!) -> Bool {
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.