¿Cómo puedo lidiar con la desaprobación de la inferencia @objc con #selector() en Swift 4?

Resuelto LinusGeffarth asked hace 7 años • 5 respuestas

Estoy intentando convertir el código fuente de mi proyecto de Swift 3 a Swift 4. Una advertencia que me da Xcode es sobre mis selectores.

Por ejemplo, agrego un objetivo a un botón usando un selector normal como este:

button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)

Esta es la advertencia que muestra:

El argumento de '#selector' se refiere al método de instancia 'myAction()' en 'ViewController' que depende de la inferencia del atributo '@objc' obsoleto en Swift 4

Agregue '@objc' para exponer este método de instancia a Objective-C

Ahora, presionar Fixel mensaje de error hace esto en mi función:

// before
func myAction() { /* ... */ }

// after
@objc func myAction() { /* ... */ }

Realmente no quiero cambiar el nombre de todas mis funciones para incluir la @objcmarca y supongo que no es necesario.

¿Cómo reescribo el selector para hacer frente a la desaprobación?


Pregunta relacionada:

  • ¿El uso de la inferencia Swift 3 @objc en el modo Swift 4 está obsoleto?
LinusGeffarth avatar Jun 06 '17 19:06 LinusGeffarth
Aceptado

La solución es correcta: no hay nada sobre el selector que pueda cambiar para que el método al que se refiere esté expuesto a Objective-C.

El motivo de esta advertencia en primer lugar es el resultado de SE-0160 . Antes de Swift 4, internalo superior, se infería que los miembros compatibles con Objective-C de NSObjectlas clases heredadas estaban @objcexpuestos a Objective-C, lo que permitía llamarlos mediante selectores (ya que se requiere el tiempo de ejecución de Obj-C para buscar el método). implementación para un selector determinado).

Sin embargo, en Swift 4, este ya no es el caso. Ahora sólo se infiere que declaraciones muy específicas son @objc, por ejemplo, anulaciones de @objcmétodos, implementaciones de @objcrequisitos de protocolo y declaraciones con atributos que implican @objc, como @IBOutlet.

La motivación detrás de esto, como se detalla en la propuesta vinculada anteriormente , es, en primer lugar, evitar que las sobrecargas de métodos en NSObjectlas clases heredadas colisionen entre sí debido a que tienen selectores idénticos. En segundo lugar, ayuda a reducir el tamaño binario al no tener que generar procesadores para miembros que no necesitan estar expuestos a Obj-C y, en tercer lugar, mejora la velocidad de los enlaces dinámicos.

Si desea exponer un miembro a Obj-C, debe marcarlo como @objc, por ejemplo:

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        button.addTarget(self, action: #selector(foo), for: .touchUpInside)
    }

    @objc func foo() {
       // ... 
    }
}

(el migrador debería hacer esto automáticamente con los selectores cuando se ejecuta con la opción "minimizar inferencia" seleccionada)

Para exponer un grupo de miembros a Obj-C, puede utilizar @objc extension:

@objc extension ViewController {

    // both exposed to Obj-C
    func foo() {}
    func bar() {}
}

Esto expondrá todos los miembros definidos en él a Obj-C y dará un error en cualquier miembro que no pueda exponerse a Obj-C (a menos que esté marcado explícitamente como @nonobjc).

Si tiene una clase en la que necesita que todos los miembros compatibles con Obj-C estén expuestos a Obj-C, puede marcar la clase como @objcMembers:

@objcMembers
class ViewController: UIViewController {
   // ...
}

Ahora, todos los miembros que se pueda inferir que lo serán @objclo serán. Sin embargo, no recomendaría hacer esto a menos que realmente necesite que todos los miembros estén expuestos a Obj-C, dadas las desventajas mencionadas anteriormente de tener miembros expuestos innecesariamente.

Hamish avatar Jun 06 '2017 13:06 Hamish

A partir de Swift 4.2, creo que todo lo que necesitas hacer es asignarlo @IBActiona tu método y evitar la @objcanotación.

let tap  =  UITapGestureRecognizer(target: self, action: #selector(self.cancel))

@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}
Logan Sease avatar Sep 26 '2018 22:09 Logan Sease

Como ya se mencionó en otras respuestas, no hay forma de evitar la @objcanotación de los selectores.

Pero la advertencia mencionada en el OP se puede silenciar siguiendo los siguientes pasos:

  1. Ir a configuración de compilación
  2. Busque la palabra clave @objc
  3. Establezca el valor de la interfaz @objc de Swift 3 enOff

A continuación se muestra la captura de pantalla que ilustra los pasos mencionados anteriormente:

Silenciar la advertencia "Interfaz Swift 3 @objc"

Espero que esto ayude

S1LENT WARRIOR avatar Nov 23 '2018 00:11 S1LENT WARRIOR