Selector, su evolución en Swift

Antes de ver la evolución de los selectores en Swift, veamos qué es un selector en Objective-C.

Los selectores en Objective-C son del tipo SEL y es la manera mediante la cual podemos identificar/seleccionar un método y luego poder ejecutarlo en un objeto.

Las formas más comunes de obtener un selector son a través de la directiva @selector y la función NSSelectorFromString, veamos como se utilizan ambas:

SEL selector1 = @selector(calcularArea);
SEL selector2 = NSSelectorFromString(@"calcularArea");
SEL selector3 = @selector(avanzar:);
SEL selector4 = NSSelectorFromString(@"avanzar:");

En las líneas 1 y 2 representamos a un método que no tiene argumentos “calcularArea” y en la 3 y 4 representamos a un método con un argumento “avanzar:”.

Generalmente se utiliza @selector en tiempo de compilación cuando conocemos la firma del método que queremos representar con el selector. NSSelectorFromString nos permite obtener el selector desde un string por lo que es más común utilizarlo en instancias donde no sabemos la firma del método hasta tiempo de ejecución.

De cierta manera podemos ver a un selector de una forma similar a un puntero dinámico a una función (aunque no es lo mismo).

Supongamos que tenemos un selector como el del ejemplo anterior para el método “calcularArea” y tenemos las clases Cuadrado, Rectangulo, Circulo y Triangulo y todas ellas tienen una implementación para un método “calcularArea”, en este caso podemos utilizar el selector en instancias de estas clases para ejecutar este método aún cuando la implementación sea diferente en cada clase. Otra forma de lograr lo mismo, en este caso, puede ser utilizando herencia y polimorfismo, pero en el caso donde las clases no tengan una clase base común vendría muy bien el uso de los selectores.

Para ejecutar un selector en una instacia de un objeto podemos hacerlo utilizando performSelector del protocolo NSObject, de la siguiente manera:

SEL selector = @selector(calcularArea);

[cuadrado performSelector:selector];
[circulo performSelector:selector];
[triangulo performSelector:selector];

// cuadrado, circulo y triangulo son instancias de
// las clases Cuadrado, Circulo y Triangulo respectivamente

Revisa la documentación de NSObject para ver otras variantes de performSelector que permiten manejo de argumentos.

Swift

En las primeras versiones no se contaba con una directiva como @selector, la única opción que teníamos era construirlo a partir de un string utilizando la estructura Selector.

Utilizaremos un ejemplo de la documentación de Apple para ilustrar esta “evolución”. Vamos a tomar una cadena de caracteres NSString y utilizando el método lowercaseStringWithLocale: convertiremos los caracteres en minúsculas ejecutando performSelector en el objeto NSString:


// 1
let cadena: NSString = "Hola, soy de tipo NSString."
let localizacion = NSLocale.currentLocale()
// 2
let selector = Selector("lowercaseStringWithLocale:")
// 3
if let resultado = cadena.performSelector(selector, withObject: localizacion) {
print(resultado.takeUnretainedValue())
}

  1. Creamos una instancia de NSString, le asignamos el valor “Hola, soy de tipo NSString.” y obtenemos la configuración de localización del usuario, que vamos a necesitar como argumento del método que vamos a invocar con el selector.
  2. Creamos nuestro selector, utilizando la estructura Selector la cual recibe la firma del método a través de un argumento string, igual que NSSelectorFromString de Objective-C.
  3. Finalmente ejecutamos el selector sobre el objeto “cadena” utilizando el método performSelector, note que esta sobrecarga de performSelector permite pasar un argumento al método que se va a ejecutar.

En Swift 2.2 se introdujo una nueva directiva #selector que viene un poco a llenar el espacio que había ante la ausencia de @selector de Objective-C y a su vez hacer que los selectores sean más seguros, la idea es básicamente igual a @selector, veamos el mismo ejemplo pero utilizando #selector.


let cadena: NSString = "Hola, soy de tipo NSString."
let localizacion = NSLocale.currentLocale()
let selector = #selector(NSString.lowercaseStringWithLocale(_:))
if let resultado = cadena.performSelector(selector, withObject: localizacion) {
print(resultado.takeUnretainedValue())
}

Lo único que cambiamos fue cómo obtenemos el selector, ahora utilizando #selector. La desventaja de utilizar un string, como cuando usamos Selector, es que no sabemos si el string realmente representa la firma del método o no hasta que esté ejecutándose el código, o se, en tiempo de ejecución, pues pudimos haber escrito “lowerStringWithLocal” en lugar de “lowercaseStringWithLocale”; esto no quiere decir que la variante del string no tenga su utilidad en ciertos casos.

#selector funciona igual que @selector solo que la forma de proveer la firma del método es utilizando sintaxis de Swift.

Pueden ver la propuesta SE-0022 que fue la que propuso la inclusión de #selector en el sitio de Swift Programming Language Evolution.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s