the - swift wikipedia
Llamar a un método de una cadena en Swift (3)
versión swift3
class MyClass:NSObject
{
required public override init() { print("Hi!") }
public func test(){
print("This is Test")
}
public class func static_test(){
print("This is Static Test")
}
}
if let c: NSObject.Type = NSClassFromString("TestPerformSelector.MyClass") as? NSObject.Type{
let c_tmp = c.init()
c_tmp.perform(Selector("test"))
c.perform(Selector("static_test"))
}
Llamar a un método desde su nombre (en un formato de cadena) puede ser útil a veces.
En Swift se recomienda cambiar el comportamiento y utilizar cierres para hacer algo "dinámicamente", por lo que puede tener un diccionario de funciones, con el nombre como clave y la implementación como valor.
Sin embargo, a veces simplemente quiere saber "cómo hacerlo", y esta es la razón de esta pregunta.
Entonces, ¿cómo llamar dinámicamente un método Swift a partir de su nombre como cadena?
En Objective C fue simple:
[self performSelector:NSSelectorFromString(@"aSelector")];
Pero performSelector
está prohibido en Swift
¿Hay alguna alternativa?
En Swift, debe usar cierres y cambiar su enfoque. Sin embargo, si desea utilizar performSelector
para llamar dinámicamente a un método dado solo su firma de cadena, aunque no es compatible de forma nativa, he encontrado cómo hacerlo.
Es posible crear una alternativa C para realizarSelector que:
- funciona incluso en clases rápidas nativas (no objetivo-c)
- toma un selector de cadena
Sin embargo, no es tan fácil implementar una versión completa, y es necesario crear el método en C.
en C tenemos dlsym (), una función que devuelve un puntero a una función dado el símbolo de char. Bueno, leyendo esta interesante publicación: http://www.eswick.com/2014/06/inside-swift/ He aprendido muchas cosas interesantes sobre swift.
Los métodos de instancias Swift son funciones simples con una firma específica, como esta
_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString
donde el valor "self" se pasa como el último parámetro
en resumen, puede llamarlo directamente desde el lado c sin ningún tipo de puente, es suficiente para reconstruir la firma de la función correcta. En la firma anterior, está el nombre del proyecto (FirstSwiftTest) y la duración (14), el nombre de la clase (ASampleClass) y la duración (12), el nombre de la función (aTestFunction) y la duración (13 ), luego otros valores como el tipo de retorno ecc ecc. Para otros detalles mira el enlace anterior
La función anterior, es la representación de esto:
class ASampleClass
{
func aTestFunction() -> NSString
{
println("called correctly")
return NSString(string: "test")
}
}
Bueno, en el lado c, pude crear esta función
#include <stdio.h>
#include <dlfcn.h>
typedef struct objc_object *id;
id _performMethod(id stringMethod, id onObject)
{
// ...
// here the code (to be created) to translate stringMethod in _TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString
// ...
id (*functionImplementation)(id);
*(void **) (&functionImplementation) = dlsym(RTLD_DEFAULT, "_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString");
char *error;
if ((error = dlerror()) != NULL) {
printf("Method not found /n");
} else {
return functionImplementation(onObject); // <--- call the function
}
return NULL
}
Y luego lo llamó por el lado rápido
let sampleClassInstance = ASampleClass()
println(_performMethod("aTestFunction", sampleClassInstance))
La función dio como resultado esta declaración impresa en el registro:
llamado correctamente
prueba
Por lo tanto, no debería ser tan difícil crear una alternativa _performMethod () en C que:
- crea automáticamente la firma de la función (ya que parece tener una lógica :-)
- maneja diferentes tipos y parámetros de valor de retorno
EDITAR
En Swift 2 (y tal vez en Beta3, no lo intenté) Parece que performSelector () está permitido (y puede llamarlo solo en las subclases de NSObject). Examinando el binario, parece que ahora Swift crea funciones estáticas que se pueden llamar específicamente mediante performSelector.
Creé esta clase
class TestClass: NSObject {
func test() -> Void {
print("Hello");
}
}
let test = TestClass()
let aSel : Selector = NSSelectorFromString("test")
test.performSelector(aSel)
y ahora en el binario encuentro
000000010026d830 t __TToFC7Perform9TestClass4testfT_T_
En este momento, no entiendo bien las razones detrás de esto, pero investigaré más a fondo
Puede llamar a un método de una cadena de esta manera:
let selectorName = "name"
perform(Selector(selectorName))
Está disponible solo cuando su clase es una subclase de NSObject