从Swift中的String调用方法

Lom*_*baX 40 swift

从名称(以String格式)调用方法有时很有用.
在Swift中,建议改变行为并使用闭包来"动态地"执行某些操作,因此例如,您可以使用函数字典,将名称作为键,将实现作为值.
但是,有时你想简单地知道"怎么做",这就是这个问题的原因.
那么,如何从名称作为字符串开始动态调用Swift方法?

在Objective C中它很简单:

[self performSelector:NSSelectorFromString(@"aSelector")];
Run Code Online (Sandbox Code Playgroud)

performSelector在Swift中被禁止
有没有其他选择?

Lom*_*baX 33

在Swift中,您应该使用闭包并改变您的方法.但是,如果你想使用performSelector动态调用只给它的String签名的方法,尽管它本身不支持,我已经找到了如何做到这一点.

可以为performSelector创建一个C替代方法:

  • 甚至可以在本地快速课程上工作(非目标-c)
  • 从字符串中取一个选择器

然而,实现它的完整版本并不是那么简单,并且有必要在C中创建该方法.

在C中我们有dlsym(),一个返回指向char符号的函数的指针的函数.好吧,阅读这篇有趣的帖子:http: //www.eswick.com/2014/06/inside-swift/ 我学到了很多关于swift的有趣的东西.

Swift实例方法是具有特定签名的普通函数,如下所示

_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString
Run Code Online (Sandbox Code Playgroud)

其中"self"值作为最后一个参数传递

总之,你可以直接从c方面调用它而不需要任何桥接,重建正确的函数签名就足够了.在上面的签名,则该项目(FirstSwiftTest)和lenght(14)的名称,类的名称(ASampleClass)和lenght(12),功能(aTestFunction)和lenght的名称(13 ),然后其他值作为返回类型ecc ecc.有关其他详细信息,请查看上一个链接

上面的函数是这个的表示:

class ASampleClass
{
    func aTestFunction() -> NSString
    {
        println("called correctly")
        return NSString(string: "test")
    }
Run Code Online (Sandbox Code Playgroud)

}

好吧,在c方面,我能够创建这个功能

#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
}
Run Code Online (Sandbox Code Playgroud)

然后迅速调用它

    let sampleClassInstance = ASampleClass()

    println(_performMethod("aTestFunction", sampleClassInstance))
Run Code Online (Sandbox Code Playgroud)

该函数导致这些语句打印在日志中:

称为正确
测试

因此,在C中创建一个_performMethod()替代方案应该不那么困难:

  • 自动创建函数签名(因为它似乎有一个逻辑:-)
  • 管理不同的返回值类型和参数

编辑

在Swift 2中(也许在Beta3中,我没有尝试)似乎允许performSelector()(并且你只能在NSObject子类上调用它).检查二进制文件,现在似乎Swift创建了可由performSelector专门调用的静态函数.

我创建了这个类

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

let test = TestClass()
let aSel : Selector = NSSelectorFromString("test")
test.performSelector(aSel)
Run Code Online (Sandbox Code Playgroud)

现在我发现二进制文件

000000010026d830 t __TToFC7Perform9TestClass4testfT_T_

在这个时候,我不明白这背后的原因,但我会进一步调查

  • 可能想澄清该类必须继承自“NSObject”才能获得可用的“performSelector”方法。 (2认同)

And*_*kyi 6

您可以通过这种方式从String调用方法:

let foo = <some NSObject subclass instance>
let selectorName = "name"
foo.perform(Selector(selectorName))
Run Code Online (Sandbox Code Playgroud)

仅当您的类是子类时才可用 foo


小智 5

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"))
}
Run Code Online (Sandbox Code Playgroud)