¿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:

LinusGeffarth
fuente
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.

Hamish
fuente
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).

Kiran Sarvaiya
fuente
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)
}
Logan Sease
fuente
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

SOLDADO GUERRERO
fuente
¿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.

Renjish C
fuente