一尘不染

在Swift中从String调用方法

swift

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

在目标C中很简单:

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

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


阅读 333

收藏
2020-07-07

共1个答案

一尘不染

在Swift中,您应该使用闭包并更改方法。但是,如果要performSelector仅使用字符串签名就可以动态调用方法,尽管本机不支持该方法,我已经找到了实现方法。

可以创建C语言替代performSelector:

  • 甚至可以在本机Swift类上运行(非Objective-C)
  • 从字符串中选择一个

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

在C语言中,我们有dlsym()这个函数,该函数返回给定给定char符号的函数的指针。好吧,阅读这篇有趣的文章:http : //www.eswick.com/2014/06/inside-
swift/ 我已经学到了很多关于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_

目前,我还不太了解其背后的原因,但我将进一步调查

2020-07-07