从Swift中的String调用方法

时间:2014-06-16 13:46:16

标签: swift

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

在目标C中,这很简单:

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

但是performSelector在Swift中被禁止了 还有其他选择吗?

3 个答案:

答案 0 :(得分:31)

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

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

  • 甚至可以在本地swift类(非objective-c)
  • 上工作
  • 从字符串
  • 中选择一个选择器

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

在C中我们有dlsym()函数,它返回一个指向char符号的函数的指针。 那么,阅读这篇有趣的帖子: http://www.eswick.com/2014/06/inside-swift/ 我学到了很多关于斯威夫特的有趣事情。

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

_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString

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

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

上面的函数代表了这个:

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

}

好吧,在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
}

然后迅速调用它

    let sampleClassInstance = ASampleClass()

    println(_performMethod("aTestFunction", sampleClassInstance))

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

正确调用
测试

因此在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)

现在在二进制文件中找到

000000010026d830 t __TToFC7Perform9TestClass4testfT_T _

目前,我并不清楚这背后的原因,但我会进一步调查

答案 1 :(得分:5)

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

let selectorName = "name"
perform(Selector(selectorName))

仅当您的类是NSObject

的子类时才可用

答案 2 :(得分:3)

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