¿Cómo resuelvo el error de compilación "uso ambiguo de" con la sintaxis de Swift #selector?
[NOTA Esta pregunta se formuló originalmente en Swift 2.2. Se ha revisado para Swift 4, lo que implica dos cambios importantes en el lenguaje: el primer parámetro de método externo ya no se suprime automáticamente, y un selector debe exponerse explícitamente a Objective-C.]
Digamos que tengo estos dos métodos en mi clase:
@objc func test() {}
@objc func test(_ sender:AnyObject?) {}
Ahora quiero usar la nueva sintaxis #selector
de Swift 2.2 para hacer un selector correspondiente al primero de estos métodos, func test()
. ¿Cómo lo hago? Cuando intento esto:
let selector = #selector(test) // error
... Obtengo un error, " Uso ambiguo de test()
."Pero si digo esto:
let selector = #selector(test(_:)) // ok, but...
... el error desaparece, pero ahora estoy refiriendo a la método incorrecto, el con un parámetro. Quiero hacer referencia al sin ningún parámetro. ¿Cómo lo hago?
[Nota: el ejemplo no es artificial. NSObject tiene ambos métodos de instancia Objective-C copy
y copy:
, Swift copy()
y copy(sender:AnyObject?)
; por lo que el problema puede surgen fácilmente en la vida real.]
1 answers
[NOTA Esta respuesta fue formulada originalmente bajo Swift 2.2. Se ha revisado para Swift 4, lo que implica dos cambios importantes en el lenguaje: el primer parámetro de método externo ya no se suprime automáticamente, y un selector debe exponerse explícitamente a Objective-C.]
Puede solucionar este problema mediante fundiendo su referencia de función a la firma de método correcta:
let selector = #selector(test as () -> Void)
(Sin embargo, en mi opinión, no deberías tener que hacer esto. Me considere esta situación como un error, revelando que la sintaxis de Swift para referirse a funciones es inadecuada. Presenté un informe de error, pero fue en vano.)
Solo para resumir la nueva sintaxis #selector
:
El propósito de esta sintaxis es evitar los bloqueos de tiempo de ejecución demasiado comunes (típicamente "selector no reconocido") que pueden surgir cuando se suministra un selector como una cadena literal. #selector()
toma una referencia de función , y el compilador comprobará que la función realmente existe y resolverá la referencia a un selector de Objective-C para usted. Por lo tanto, no puede cometer ningún error fácilmente.
(EDITAR: Bien, sí puedes. Puede ser un lunkhead completo y establecer el destino en una instancia que no implemente el mensaje de acción especificado por #selector
. El compilador no te detendrá y te bloquearás como en los viejos tiempos. Suspiro...)
Una referencia de función puede aparecer en cualquiera de tres formas:
-
El nombre desnudo de la función. Esto es suficiente si la función no es ambigua. Así, por ejemplo:
@objc func test(_ sender:AnyObject?) {} func makeSelector() { let selector = #selector(test) }
Solo hay un método
test
, por lo que este#selector
se refiere a él a pesar de que toma un parámetro y el#selector
no menciona el parámetro. El selector de Objective-C resuelto, detrás de las escenas, seguirá siendo correctamente"test:"
(con los dos puntos, indicando un parámetro). -
El nombre de La función, junto con el resto de su firma. Para ejemplo:
func test() {} func test(_ sender:AnyObject?) {} func makeSelector() { let selector = #selector(test(_:)) }
Tenemos dos métodos
test
, por lo que necesitamos diferenciar; la notacióntest(_:)
se resuelve con el segundo , el que tiene un parámetro. -
El nombre de la función con o sin el resto de su firma, más un cast para mostrar los tipos de los parámetros. Así:
@objc func test(_ integer:Int) {} @nonobjc func test(_ string:String) {} func makeSelector() { let selector1 = #selector(test as (Int) -> Void) // or: let selector2 = #selector(test(_:) as (Int) -> Void) }
Aquí, hemos sobrecargado
test(_:)
. La sobrecarga no puede ser expuesta a Objective-C, porque Objective-C no permite sobrecarga, por lo que solo uno de ellos está expuesto, y podemos formar un selector solo para el que está expuesto, porque los selectores son una característica de Objective-C. Pero debemos todavía desambiguar en lo que respecta a Swift, y el elenco hace eso.(Es esta característica lingüística la que se utiliza - mal utilizada, en mi opinión - como la base de la respuesta anterior.)
Además, es posible que tenga que ayudar a Swift a resolver la referencia de la función diciéndole qué clase la función está en:
Si la clase es la misma que esta, o arriba de la cadena de superclase de esta, normalmente no se necesita más resolución (como se muestra en los ejemplos anteriores); opcionalmente, puede decir
self
, con notación de puntos (por ejemplo,#selector(self.test)
, y en algunas situaciones puede que tenga que hacerlo.-
De lo contrario, se usa una referencia a una instancia para la que se implementa el método, con notación de puntos, como en este ejemplo de la vida real (
self.mp
es un MPMusicPlayerController):let pause = UIBarButtonItem(barButtonSystemItem: .pause, target: self.mp, action: #selector(self.mp.pause))
...o puede usar el nombre de la clase , con notación de punto:
class ClassA : NSObject { @objc func test() {} } class ClassB { func makeSelector() { let selector = #selector(ClassA.test) } }
(Esto parece una notación curiosa, porque parece que estás diciendo
test
es un método de clase en lugar de un método de instancia, pero se resolverá correctamente a un selector, que es todo lo que importa.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-24 16:35:59