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


131

Estoy tratando de 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 regular 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 de atributo '@objc' en desuso en Swift 4

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

Ahora, presionar Fixel mensaje de error 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 @objcmarca y supongo que no es necesario.

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


Pregunta relacionada:


3
No, marcarlos como ahora @objc es necesario para exponerlos a Obj-C y, por lo tanto, usarlos con selectores.
— Hamish

3
Entonces, ¿la parte en desuso está inferiendo funciones de acceso público como @objc? Eso es un poco molesto, pero generalmente hago que estas funciones sean privadas, lo que requiere que lo marque como de @objctodos modos.
— Connor

2
Consulte SE-0160 para obtener más información sobre el cambio. Otra alternativa es marcar su clase dada como @objcMemberspara exponer a todos los miembros compatibles con Obj-C a Obj-C, pero no lo recomendaría a menos que realmente necesite que toda su clase esté expuesta.
— Hamish

3
@LinusGeffarth Como se dijo en la propuesta, es probable que aumente innecesariamente el tamaño de su enlace binario y dinámico que llevará más tiempo. Realmente no creo que sea demasiado complicado para la claridad añadida que quieres decir específicamente para que algo particular se use desde Obj-C.
— Hamish

1
Probado, no funciona.
— LinusGeffarth

Respuestas:


156

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

Toda la razón de esta advertencia en primer lugar es el resultado de SE-0160 . Antes de Swift 4, se dedujo que los internalmiembros compatibles de Objective-C de las NSObjectclases heredadas eran @objcy, por lo tanto, se exponían a Objective-C, lo que permitía que se los llamara utilizando selectores (ya que se requiere el tiempo de ejecución Obj-C para buscar el método implementación para un selector dado).

Sin embargo, en Swift 4, este ya no es el caso. Ahora solo se infiere que las 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 anterior , es en primer lugar evitar que las sobrecargas de métodos en las NSObjectclases heredadas choquen entre sí debido a que tienen selectores idénticos. En segundo lugar, ayuda a reducir el tamaño binario al no tener que generar thunks para los miembros que no necesitan estar expuestos a Obj-C, y en tercer lugar mejora la velocidad de la vinculación dinámica.

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 por usted con selectores cuando se ejecuta con la opción "minimizar inferencia" seleccionada)

Para exponer un grupo de miembros a Obj-C, puede usar un @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 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 pueden inferir @objcserá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.


2
¿Por qué Xcode no hace eso automáticamente al convertir el código a la sintaxis más reciente?
— LinusGeffarth

1
Me pregunto, ¿ @IBActiontambién son automáticos @objc? ese no era el caso hasta ahora, pero sería lógico. EDITAR: nvm, la propuesta establece claramente que @IBActiones suficiente para que un método sea @objc.
— Sulthan

8
No entiendo nada de esto. No tengo ningún código Objective-C. ¿No hay una forma pura y rápida de agregar objetivos a un botón? ¿O usar selectores? No quiero que mi código esté plagado de este atributo. ¿Es porque un UIButton se deriva de algún NSObject / Obj-c-thing?
— Sti

11
@Sti Entonces, para responder directamente " ¿No hay una forma pura y rápida de agregar objetivos a un botón? ¿O usar selectores? ", No, no hay una forma "pura y rápida" de usar selectores para enviar a los métodos. Confían en el tiempo de ejecución Obj-C para buscar la implementación del método para llamar a un selector en particular; el tiempo de ejecución Swift no tiene esa capacidad.
— Hamish

12
Los selectores de hombres son un desastre. Parece que cualquier otra actualización rápida están jugando con ella. ¿Por qué no podemos simplemente tener un selector de autocompletado puro y rápido para los métodos? Hace que el código se vea tan feo.
— John Riselvato

16

Como documentación oficial de Apple . necesitas usar @objc para llamar a tu 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 Selectorestructura y pueden construirse usando la #selector expresión. Para crear un selector para un método que se puede 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 Objective-C de una propiedad, pase el nombre de la propiedad con el prefijo getter:o la setter:etiqueta, como #selector(getter: MyViewController.myButton).


Todo lo que entiendo de esto es que, en algunos casos, necesito agregar @objc al inicio de una función, ya sea que lo llame o no desde Objective-C. Solo estoy aprendiendo que Swift ha usado Obj-C durante años
— SundialSoft

10

A partir de, creo Swift 4.2, todo lo que necesitas hacer es asignar @IBAction a tu método y puedes evitar esta tonta anotación @objc

`` `

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


@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}

1
Esto también le indicará al creador de interfaces que esta función se puede conectar, lo que podría no ser lo que desea. También está haciendo lo mismo que @objc.
— Josh Paradroid

2

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

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

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

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

Silenciar la advertencia "Swift 3 @objc interface"

Espero que esto ayude


¿Cómo afecta esto a su proyecto en el panorama general?
— Zonily Jame

1
¿Qué tal el tamaño * .ipa o * .xarchive y el tiempo de construcción?
— Zonily Jame

@ZonilyJame no noté el tiempo de construcción, pero no hubo ningún efecto en el tamaño de IPA
— S1LENT WARRIOR

Este no es un valor recomendado que puede usar si usa la versión swift 4.0+ según las pautas de Apple. Consulte este help.apple.com/xcode/mac/current/#/deve838b19a1
— Bhavin_m

1
Puede actualizar esta respuesta estableciendo el valor en Predeterminado en Xcode 11. Es importante establecer el valor tanto en el proyecto como en sus objetivos para eliminar completamente la advertencia.
— Tommie C.

2

Si necesita miembros objetivos c en su controlador de vista, simplemente agregue @objcMembers en la parte superior del controlador de vista. Y puede evitar esto agregando IBAction en su código.

@IBAction func buttonAction() {

}

Asegúrese de conectar esta salida en el guión gráfico.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.