在 Swift 中从字符串调用方法 [英] Call a method from a String in Swift

查看:65
本文介绍了在 Swift 中从字符串调用方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Calling a method from its name (in a String format) can be sometimes useful.
In Swift it is recomended to change behavior and to use closures to do something "dynamically", so for example you can have a dictionary of functions, with the name as the key, and the implementation as the value.
However, sometimes you want to simply know "how to do it", and this is the reason of this question.
So, how to call dynamically a Swift method starting from it's name as string?

在 Objective C 中很简单:

In Objective C it was simple:

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

但是 performSelector 在 Swift 中被禁止
有什么办法吗?

But performSelector is banned in Swift
Is there any alternative?

推荐答案

在 Swift 中,您应该使用闭包并改变您的方法.但是,如果您想使用 performSelector 动态调用仅给定字符串签名的方法,尽管本机不支持它,但我已经找到了方法.

In Swift, you should use closures and change your approach. However, if you want to use performSelector to dynamically call a method given only it's String signature, altough it's not supported natively, I've found how to do it.

可以创建一个 C 替代 performSelector :

It is possible to create a C alternative to performSelector that:

  • 甚至适用于原生 swift 类(非 Objective-c)
  • 从字符串中获取选择器

然而,实现它的完整版本并不是那么简单,需要用 C 语言创建方法.

However it's not so straightforward to implement a complete version of it, and it's necessary to create the method in C.

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

in C we have dlsym(), a function that returns a pointer to a function given the char symbol. Well, reading this interesting post: http://www.eswick.com/2014/06/inside-swift/ I've learned a lot of interesting things about swift.

Swift 实例方法是具有特定签名的普通函数,就像这样

Swift instance methods are plain functions with a specific signature, like this

_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString

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

where the "self" value is passed as the last parameter

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

in short you can call it directly from the c side without any kind of bridging, it is sufficient to rebuild the correct function signature. In the signature above, there is the name of the project (FirstSwiftTest) and the lenght (14), the name of the class (ASampleClass) and the lenght (12), the name of the function (aTestFunction) and the lenght (13), then other values as the return type ecc ecc. For other details look at the previous link

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

The function above, is the representation of this:

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

}

嗯,在 c 方面,我能够创建这个函数

Well, on the c side, I was able to create this function

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

然后在swift端调用它

And then called it on the swift side

    let sampleClassInstance = ASampleClass()

    println(_performMethod("aTestFunction", sampleClassInstance))

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

The function resulted in these statement printed on the log:

正确调用
测试

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

So it should be not so difficult to create a _performMethod() alternative in C that:

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

编辑

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

In Swift 2 (and maybe in Beta3, I didn't try) It seems that performSelector() is permitted (and you can call it only on NSObject subclasses). Examining the binary, It seems that now Swift creates static functions that can be specifically called by performSelector.

我创建了这个类

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

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

现在在二进制文件中我找到

and now in the binary I find

000000010026d830 t __TToFC7Perform9TestClass4testfT_T_

000000010026d830 t __TToFC7Perform9TestClass4testfT_T_

目前我不太明白这背后的原因,但我会进一步调查

At this time, I don't understand well the reasons behind this, but I'll investigate further

这篇关于在 Swift 中从字符串调用方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆