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

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

Por ejemplo, agrego un objective a un botón usando un selector regular como este:

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

Esta es la advertencia que muestra:

El argumento de ‘#selector’ hace referencia al método de instancia ‘myAction ()’ en ‘ViewController’ que depende de la inferencia del atributo ‘@objc’ obsoleta en Swift 4

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

Ahora, presionar Fix en el mensaje de error le hace esto a mi función:

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

Realmente no quiero cambiar el nombre de todas mis funciones para incluir la marca @objc y @objc que eso no es necesario.

¿Cómo reescribo el selector para lidiar con la depreciación?


Pregunta relacionada:

  • ¿El uso de la inferencia de Swift 3 @objc en el modo Swift 4 está en desuso?

La solución es correcta: no hay nada sobre el selector que pueda cambiar para hacer 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, internal miembros internal o superiores compatibles con Objective-C de las clases NSObject de NSObject se dedujeron como @objc y, por lo tanto, se exponían a Objective-C, lo que les permitía llamarse utilizando selectores (ya que el tiempo de ejecución de Obj-C para buscar la implementación del método para un selector dado).

Sin embargo, en Swift 4, este ya no es el caso. Ahora se infiere que solo las declaraciones muy específicas son @objc , por ejemplo, anulaciones de los métodos @objc , implementaciones de los requisitos del protocolo @objc y declaraciones con atributos que implican @objc , como @IBOutlet .

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

Si desea exponer a 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 debe hacer esto automáticamente con selectores cuando se ejecuta con la opción “minimizar inferencia” seleccionada)

Para exponer un grupo de miembros a Obj-C, puede usar una @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 a cualquier miembro que no pueda ser expuesto a Obj-C (a menos que esté marcado explícitamente como @nonobjc ).

Si tiene una clase donde 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 puede inferir que serán @objc serán. Sin embargo, no recomendaría hacer esto a menos que realmente necesite a todos los miembros expuestos a Obj-C, dado los inconvenientes mencionados anteriormente de tener miembros innecesariamente expuestos.

Como documentación oficial de Apple . necesita usar @objc para llamar a su Método Selector.

En Objective-C, un selector es un tipo que se refiere al nombre de un método Objective-C. En Swift, los selectores Objective-C están representados por la estructura Selector , y pueden construirse utilizando la expresión #selector . Para crear un selector para un método al que se pueda llamar desde Objective-C, pase el nombre del método, como #selector(MyViewController.tappedButton(sender:)) . Para construir un selector para el método getter o setter de Objective-C de una propiedad, pase el nombre de propiedad prefijado por getter: o setter: label, como #selector(getter: MyViewController.myButton) .