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 internal
miembros compatibles de Objective-C de las NSObject
clases heredadas eran @objc
y, 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 @objc
métodos, implementaciones de @objc
requisitos 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 NSObject
clases 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 @objc
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.
@objc
es necesario para exponerlos a Obj-C y, por lo tanto, usarlos con selectores.