Error del compilador: el método con el selector Objective-C entra en conflicto con la declaración anterior con el mismo selector Objective-C

209

Estoy empezando a aprender Swift y he seguido las muy buenas conferencias en video de la Universidad de Stanford en YouTube. Aquí hay un enlace si está interesado o ayuda (aunque no es necesario que comprenda mi problema):

Desarrollo de aplicaciones iOS 8 con Swift - 2. Más Xcode y Swift, MVC

Mientras seguía las conferencias llegué a un punto en el que (por lo que podía ver) mi código era idéntico al código del video, pero en mi sistema recibí un error del compilador. Después de muchas pruebas y errores, he logrado reducir mi código a dos ejemplos, uno de los cuales genera un error, el otro o el que no, pero no tengo idea de qué está causando el error o cómo resolverlo.

El código que crea el error es:

import UIKit

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

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

Esto crea el siguiente error del compilador:

Método 'perform' con el selector Objective-C 'perform:' entra en conflicto con la declaración anterior con el mismo selector Objective-C

Simplemente eliminando la subclasificación de UIViewController, el código compila:

import UIKit

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

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

Otra información que puede o no ser relevante:

  • Recientemente he actualizado a Yosemite.
  • Cuando instalé Xcode, terminé con una versión Beta (Versión 6.3 (6D543q)) porque (si no recuerdo mal) esta era la versión que necesitaba ejecutar en mi versión de OS X.

Estoy medio esperando que esto sea un error en el compilador porque de lo contrario esto no tiene ningún sentido para mí. Cualquier ayuda muy agradecida recibida!

Auspicio
fuente
3
Puede ejecutar Xcode 6.2 en Yosemite. Puede descargarlo de la tienda de aplicaciones y puede vivir en su sistema con la versión Beta. No recomendaría usar Xcode 6.3 para la clase Stanford en este momento porque es beta e incluye Swift 1.2, que es diferente de la versión anterior de Swift utilizada en los videos.
vacawama
2
La respuesta (actualmente aceptada) del usuario (feb) del 5 de abril ya no es la mejor. En cambio, la respuesta de (James Zhang) del 16 de abril es más específica y correcta.
phlebotinum

Respuestas:

144

Objective-C no admite la sobrecarga de métodos, debe usar un nombre de método diferente. Cuando heredaste UIViewController, heredaste NSObject e hiciste la clase interopable con Obj-C. Swift, por otro lado, admite la sobrecarga, es por eso que funciona cuando elimina la herencia.

feb
fuente
2
Anulación del método de APOYOS Objective-C (con una estafa de advertencias del compilador (suprimible) que le notifica sobre sobrecargar algo ya implementado), Apple simplemente no quiere que lo haga para evitar que sus marcos se sobrecarguen. Estoy usando tales sobrecargas fe UIFonttodos los días.
Michi
La respuesta de @polarwar a continuación es la mejor para Swift 2: stackoverflow.com/a/31500740/144088
Crashalot el
237

Yo mismo también estoy tomando el curso de Standford y me quedé atrapado aquí por mucho tiempo también, pero después de buscar, encontré algo desde aquí: notas de lanzamiento de Xcode y mencionó algo a continuación:

Swift 1.2 es estricto sobre la comprobación de la sobrecarga basada en tipos de los métodos e inicializadores @objc, algo que no es compatible con 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))
}

Este código funcionaría cuando se invoca desde Swift, pero podría bloquearse fácilmente si se invoca desde Objective-C. Para resolver este problema, use un tipo que no sea compatible con Objective-C para evitar que el compilador Swift exponga al miembro al tiempo de ejecución de Objective-C:

  • Si tiene sentido, marque el miembro como privado para deshabilitar la inferencia de @objc.
  • De lo contrario, use un parámetro ficticio con un valor predeterminado, por ejemplo: _ nonobjc: () = (). (19826275)

No se infiere que las anulaciones de métodos expuestos a Objective-C en subclases privadas son @objc, lo que hace que el compilador Swift se bloquee. Agregue explícitamente el atributo @objc a cualquiera de estos métodos de anulación. (19935352)

Los símbolos de los SDK no están disponibles cuando se usa Abrir rápidamente en un proyecto o espacio de trabajo que usa Swift. (20349540)

lo que hice fue agregar "privado" frente al método de anulación como este:

    private func performOperation(operation: Double -> Double) {
    if operandStack.count >= 1 {
        displayValue = operation(operandStack.removeLast())
        enter()
    }
}
James Zhang
fuente
3
Esta solución es la más viable que encuentro en mi humilde opinión, ya que esto tiene sentido totalmente para establecer este método privada
demental
38
Tenga en cuenta que ahora también hay un atributo @nonobjc, que se puede usar para excluir un método del tiempo de ejecución de Objective-C.
Erik J
2
Apoyo el comentario de @ ErikJ y la respuesta de polarwar a continuación. Esta parece ser la mejor respuesta para avanzar con Swift 2 y xcode 7. Si aún no lo ha actualizado, lo recomiendo encarecidamente.
Austin A
La respuesta de @polarwar a continuación es la mejor para Swift 2: stackoverflow.com/a/31500740/144088
Crashalot el
111

Como ya se ha respondido, ObjC no admite la sobrecarga de métodos (dos métodos con el mismo nombre) y en Swift 2 bajo Xcode 7 hay dos opciones para resolver este tipo de problemas. Una opción es renombrar el método usando el atributo:@objc(newNameMethod:)

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

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

Otra opción para resolver este problema en Xcode 7+ es aplicar el @nonobjcatributo a cualquier método, subíndice o inicializador.

func methodOne() {...}

@nonobjc
func methodOne() {...}
polarware
fuente
66
Esto resuelve el problema para Swift 2 (y arriba). debe actualizarse como la respuesta más correcta. ty.
Maxim Veksler
2
Para cualquiera que use Swift 2 y Xcode 7 +, esta es la respuesta correcta. Estoy de acuerdo con polarwar
TerNovi
17

El problema es UIViewControlleres una @objcclase. Al heredar de UIViewController, BugViewControllertambién es un@objc clase.

Esto significa que debe cumplir con las reglas de los selectores Objective-C (el nombre de un método). Los métodos func perform(operation: (Double) -> Double)y func perform(operation: (Double, Double) -> Double)ambos tienen el mismo selector@selector(perform:) . Esto no esta permitido.

Para resolver esto, use diferentes nombres: me gusta func perform1(operation: (Double) -> Double)y func perform2(operation: (Double, Double) -> Double).


Creo que la mejor manera de manejar esto es darle a sus perform()métodos nombres más descriptivos. ¿Qué hacen estos métodos? ¿Cómo cambian el estado del controlador de vista? Mire los otros UIViewControllermétodos para tener una idea del estilo de denominación de métodos, o lea Los nombres de métodos deben ser expresivos y únicos dentro de una clase

Jeffery Thomas
fuente
Gracias, esto responde perfectamente a mi pregunta y, como usted fue el primero, lo marcaré como correcto.
Auspicio
Habiendo dicho eso, todavía no entiendo por qué funcionó el código de la conferencia, ¡estoy bastante seguro de que hizo lo que hizo mi código de no compilación! Hola, volveré y lo comprobaré dos veces. Debe haber algo diferente.
Auspicio
2
@Auspice Puede que no haya producido errores con la versión de Xcode que estaban usando para los videos, pero todavía era un problema. No fue hasta Xcode 6.3 que el compilador pudo detectar esto y advertirle.
Mick MacCallum
3
Paul Hegarty quiere demostrar la función de 'sobrecarga' aquí (2 funciones con el mismo nombre, pero con un conjunto diferente de argumentos), ¡así que usa el mismo nombre de método a propósito! La sobrecarga solo está permitida en Swift, no en Objective-C. Es por eso que la solución es eliminar el formulario de herencia UIViewController (que es una clase Objective-C) o declarar el método privado. Ambas soluciones se explican en detalle en las otras respuestas aquí.
Ronny Webers
En realidad, utilicé una palabra clave privada delante de la función. como, private func performOperation (operación: Doble -> Doble) {} y private func performOperation (operación: (Doble, Doble) -> Doble) {} Aquí logré la sobrecarga del método con la ayuda de PRIVATE. porque los utilicé en ViewController.Swift solamente. ¿Por qué el compilador no dice ningún error?
iTag
2

Obtuve el mismo error debido a que tengo dos métodos con la misma firma Obj-C:

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

No quería marcar a uno de ellos como @nonobjc debido a la posibilidad de consecuencias imprevistas en el tiempo de ejecución. (Alguien puede corregirme si no hay posibilidad)

Lo resolvió usando la función de nombre de parámetro externo de Swift (hice el nombre externo igual que el nombre local) para el segundo método, que efectivamente cambia la firma del método Obj-c:

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