the - Appeler une méthode depuis une chaîne dans Swift




the swift programming language french (2)

Appeler une méthode à partir de son nom (au format String) peut être parfois utile.
Dans Swift, il est recommandé de modifier le comportement et d'utiliser des fermetures pour effectuer des tâches "de manière dynamique". Vous pouvez par exemple disposer d'un dictionnaire de fonctions, avec le nom comme clé et l'implémentation comme valeur.
Cependant, vous voulez parfois simplement savoir "comment le faire", et c’est la raison de cette question.
Alors, comment appeler dynamiquement une méthode Swift à partir de son nom sous forme de chaîne?

En Objective C, c'était simple:

[self performSelector:NSSelectorFromString(@"aSelector")];

Mais performSelector est interdit à Swift
Y a-t-il une alternative?


version 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"))
}

Dans Swift, vous devez utiliser des fermetures et modifier votre approche. Cependant, si vous souhaitez utiliser performSelector pour appeler dynamiquement une méthode avec uniquement sa signature String, même si elle n'est pas prise en charge de manière native, j'ai trouvé le moyen de le faire.

Il est possible de créer une alternative C à performSelector qui:

  • fonctionne même sur les classes de swift natives (non-objective-c)
  • prend un sélecteur de chaîne

Cependant, il n'est pas si simple d'implémenter une version complète de celle-ci, et il est nécessaire de créer la méthode en C.

en C, nous avons dlsym (), une fonction qui retourne un pointeur sur une fonction à partir du symbole char. Eh bien, en lisant cet article intéressant: http://www.eswick.com/2014/06/inside-swift/ J'ai appris beaucoup de choses intéressantes sur le vif.

Les méthodes d'instance Swift sont des fonctions simples avec une signature spécifique, comme celle-ci

_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString

où la valeur "self" est passée en dernier paramètre

En bref, vous pouvez l’appeler directement du côté c sans aucune sorte de pontage, il suffit de reconstruire la signature de fonction correcte. Dans la signature ci-dessus, il y a le nom du projet (FirstSwiftTest) et la longueur (14), le nom de la classe (ASampleClass) et la longueur (12), le nom de la fonction (aTestFunction) et la longueur (13 ), puis d’autres valeurs comme le type de retour ecc ecc. Pour d'autres détails, regardez le lien précédent

La fonction ci-dessus, est la représentation de ceci:

class ASampleClass
{
    func aTestFunction() -> NSString
    {
        println("called correctly")
        return NSString(string: "test")
    }

}

Eh bien, du côté c, j'ai pu créer cette fonction

#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
}

Et puis appelé sur le côté rapide

    let sampleClassInstance = ASampleClass()

    println(_performMethod("aTestFunction", sampleClassInstance))

La fonction a eu pour résultat ces déclarations imprimées sur le journal:

appelé correctement
tester

Ainsi, il ne devrait pas être si difficile de créer une alternative _performMethod () en C qui:

  • crée automatiquement la signature de la fonction (puisqu'il semble y avoir une logique :-)
  • gère différents types et paramètres de valeur de retour

MODIFIER

Dans Swift 2 (et peut-être en Bêta3, je n'ai pas essayé) Il semble que performSelector () est autorisée (et vous ne pouvez l'appeler que dans les sous-classes NSObject). En examinant le binaire, il semble que maintenant Swift crée des fonctions statiques pouvant être appelées spécifiquement par performSelector.

J'ai créé cette classe

class TestClass: NSObject {
    func test() -> Void {
        print("Hello");
    }
}

let test = TestClass()
let aSel : Selector = NSSelectorFromString("test")
test.performSelector(aSel)

et maintenant dans le binaire je trouve

000000010026d830 t __TToFC7Perform9TestClass4testfT_T_

En ce moment, je ne comprends pas bien les raisons derrière cela, mais je vais aller plus loin







swift