Pregunta @selector () en Swift?


Estoy tratando de crear un NSTimer en Swift pero estoy teniendo algunos problemas.

NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)

test() es una función en la misma clase.


Aparece un error en el editor:

No se pudo encontrar una sobrecarga para 'init' que acepta el   argumentos

Cuando cambio selector: test() a selector: nil el error desaparece

He intentado:

  • selector: test()
  • selector: test
  • selector: Selector(test())

Pero nada funciona y no puedo encontrar una solución en las referencias.


584
2018-06-03 05:21


origen


Respuestas:


Rápido sí mismo no usa selectores: varios patrones de diseño que en Objective-C hacen uso de selectores funcionan de manera diferente en Swift. (Por ejemplo, use encadenamiento opcional en tipos de protocolo o is/as pruebas en lugar de respondsToSelector:, y usa cierres siempre que puedas en lugar de performSelector: para una mejor seguridad de tipo / memoria).

Pero todavía hay una serie de importantes API basadas en ObjC que usan selectores, incluidos temporizadores y el patrón de objetivo / acción. Swift proporciona el Selector escriba para trabajar con estos. (Swift automáticamente usa esto en lugar de ObjC SEL tipo.)

En Swift 2.2 (Xcode 7.3) y posterior (incluyendo Swift 3 / Xcode 8 y Swift 4 / Xcode 9):

Puedes construir un Selector desde un tipo de función Swift usando el #selector expresión.

let timer = Timer(timeInterval: 1, target: object,
                  selector: #selector(MyClass.test),
                  userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
                 for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
             with: button, with: otherButton)

Lo bueno de este enfoque? El compilador Swift comprueba una referencia de función, por lo que puede usar #selector expresión solo con pares de clase / método que realmente existen y que son elegibles para usar como selectores (consulte "Disponibilidad de selectores" a continuación). También puede hacer que su función sea tan específica como lo necesite, de acuerdo con las reglas Swift 2.2+ para el nombramiento de funciones.

(Esto es en realidad una mejora sobre ObjC @selector() directiva, porque el compilador -Wundeclared-selector check solo verifica que el selector nombrado existe. La referencia de función Swift a la que pasa #selector verifica la existencia, membresía en una clase y firma de tipo.)

Hay un par de advertencias adicionales para las referencias de funciones que pasa al #selector expresión:

  • Múltiples funciones con el mismo nombre base se pueden diferenciar por sus etiquetas de parámetros usando el antes mencionado sintaxis para referencias de funciones (p.ej. insertSubview(_:at:) vs insertSubview(_:aboveSubview:)) Pero si una función no tiene parámetros, la única manera de eliminar la ambigüedad es usar un as emitir con la firma de tipo de la función (p. foo as () -> () vs foo(_:))
  • Hay una sintaxis especial para los pares getter / setter de propiedades en Swift 3.0+. Por ejemplo, dado un var foo: Int, puedes usar #selector(getter: MyClass.foo) o #selector(setter: MyClass.foo).

Notas generales:

Casos donde #selector no funciona, y nombrando: A veces no tiene una referencia de función para hacer un selector con (por ejemplo, con métodos registrados dinámicamente en el tiempo de ejecución de ObjC). En ese caso, puedes construir un Selector de una cadena: p. Selector("dynamicMethod:") - aunque pierdes la verificación de validez del compilador. Cuando haces eso, debes seguir las reglas de nomenclatura de ObjC, incluidos los dos puntos (:) para cada parámetro.

Disponibilidad del selector:El método al que hace referencia el selector debe estar expuesto al tiempo de ejecución de ObjC. En Swift 4, todos los métodos expuestos a ObjC deben tener su declaración precedida del @objc atributo. (En versiones anteriores obtuviste ese atributo gratis en algunos casos, pero ahora tienes que declararlo explícitamente).

Recuerda eso private los símbolos no están expuestos al tiempo de ejecución también, su método debe tener al menos internal visibilidad.

Caminos clave: Estos están relacionados pero no exactamente como selectores. Hay una sintaxis especial para estos en Swift 3, también: p. chris.valueForKeyPath(#keyPath(Person.friends.firstName)). Ver SE-0062 para detalles. Y aún más KeyPath cosas en Swift 4, de modo que asegúrese de estar utilizando la API correcta basada en KeyPath en lugar de selectores, si corresponde.

Puede leer más sobre los selectores bajo Interactuando con las API de Objective-C en Usando Swift con Cocoa y Objective-C.

Nota: Antes de Swift 2.2, Selector conformado a StringLiteralConvertible, por lo que puede encontrar un código antiguo donde las cadenas simples se pasan a las API que toman selectores. Querrá ejecutar "Convertir a sintaxis Swift actual" en Xcode para obtener los que utilizan #selector.


865
2018-06-03 05:27



Aquí hay un ejemplo rápido sobre cómo usar el Selector clase en Swift:

override func viewDidLoad() {
    super.viewDidLoad()

    var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
    self.navigationItem.rightBarButtonItem = rightButton
}

func method() {
    // Something cool here   
}

Tenga en cuenta que si el método pasado como una cadena no funciona, fallará en tiempo de ejecución, no en tiempo de compilación y bloqueará su aplicación. Ten cuidado


78
2018-06-03 05:31



Además, si su clase (Swift) no desciende de una clase Objective-C, debe tener dos puntos al final de la cadena de nombre del método de destino y debe usar la propiedad @objc con su método de destino, p.

var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))

@objc func method() {
    // Something cool here   
} 

de lo contrario, recibirá un error de "Selector no reconocido" en el tiempo de ejecución.


42
2018-06-24 15:23



Para los lectores futuros, descubrí que tenía un problema y recibía un unrecognised selector sent to instance error que fue causado al marcar el objetivo func como privado.

los func  DEBE ser públicamente visible para ser llamado por un objeto con una referencia a un selector.


22
2018-04-10 18:08



Swift 2.2+ y Swift 3 Update

Usa el nuevo #selector expresión, lo que elimina la necesidad de utilizar literales de cadena haciendo que el uso sea menos propenso a errores. Para referencia:

Selector("keyboardDidHide:")

se convierte

#selector(keyboardDidHide(_:))

Ver también: Propuesta Swift Evolution

Nota (Swift 4.0): 

Si usas #selectornecesitarías marcar la función como @objc

Ejemplo:

@objc func something(_ sender: UIButton)


22
2018-04-06 16:06



Solo en caso de que alguien más tenga el mismo problema que tuve con NSTimer donde ninguna de las otras respuestas resolvió el problema, es realmente importante mencionarlo, si estás usando una clase que no hereda de NSObject ni directamente ni en lo más profundo de la jerarquía ( por ejemplo, archivos swift creados manualmente), ninguna de las otras respuestas funcionará incluso cuando se especifique de la siguiente manera:

let timer = NSTimer(timeInterval: 1, target: self, selector: "test", 
                    userInfo: nil, repeats: false)
func test () {}

Sin cambiar nada más que simplemente hacer que la clase herede de NSObject, dejé de obtener el error "Selector no reconocido" y logré que mi lógica funcionara como esperaba.


19
2017-07-13 05:31



Swift 4.0

usted crea el Selector como a continuación.

1.añadir el evento a un botón como:

button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)

y la función será como a continuación:

@objc func clickedButton(sender: AnyObject) {

}

15
2017-10-15 07:27



Si desea pasar un parámetro a la función desde el NSTimer, aquí está su solución:

var somethingToPass = "It worked"

let timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "tester:", userInfo: somethingToPass, repeats: false)

func tester(timer: NSTimer)
{
    let theStringToPrint = timer.userInfo as String
    println(theStringToPrint)
}

Incluya los dos puntos en el texto del selector (probador :), y su (s) parámetro (s) vaya (n) en userInfo.

Su función debe tomar NSTimer como parámetro. Luego simplemente extraiga userInfo para obtener el parámetro que pasó.


14
2017-08-07 15:29