在 Swift 中从字符串调用方法
Posted
技术标签:
【中文标题】在 Swift 中从字符串调用方法【英文标题】:Call a method from a String in Swift 【发布时间】:2014-06-16 13:46:15 【问题描述】:从方法的名称(以字符串格式)调用方法有时很有用。 在 Swift 中,建议更改行为并使用闭包“动态地”执行某些操作,例如,您可以拥有一个函数字典,名称为键,实现为值。 但是,有时您想简单地知道“如何做”,这就是这个问题的原因。 那么,如何动态调用一个 Swift 方法,从它的名字作为字符串开始呢?
在 Objective C 中很简单:
[self performSelector:NSSelectorFromString(@"aSelector")];
但是performSelector
在 Swift 中被禁止
有没有其他选择?
【问题讨论】:
只是想知道您为什么删除了答案?问答没有错 Swift 主要是一种静态语言,具有强类型,没有真正的反射。这种动态行为是不安全的,因此在 Swift 中应该避免。 Swift 目前不以任何方式支持它。此外,它不适用于 ARC(即使在 Obj-C 中也会触发警告)。你可以在 Swift 中使用 Obj-C 解决方案,但没有真正的 Swift 解决方案,因为 Swift 不喜欢这种东西。 @holex ,我已经阅读了所有文档并详细分析了 Swift ;-) 这就是我写这个问答的原因(你看到了吗?下面有我的答案)到目前为止,任何其他网站都没有该解决方案。 我知道!!您是否了解发布问题只是为了以问答方式附上答案?此外,为了确认您在说什么,我的问题从以下声明开始:“在 Swift 中,建议改变行为”。但问答的意义不同:了解它是如何工作的和为什么 @holex,*** 社区鼓励 Q+A 风格的问题,他专门提出这个问题,以便为可能有同样疑问的人自己提供答案。 【参考方案1】:在 Swift 中,您应该使用闭包并改变您的方法。
但是,如果您想使用performSelector
动态调用仅给出字符串签名的方法,尽管它本身不支持,但我已经找到了方法。
可以为 performSelector 创建一个 C 替代方案:
甚至适用于原生 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
然后在 swift 端调用它
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_
目前我还不太了解这背后的原因,但我会进一步调查
【讨论】:
可能想澄清该类必须继承自NSObject
才能获得可用的 performSelector
方法。【参考方案2】:
您可以通过这种方式从字符串中调用方法:
let foo = <some NSObject subclass instance>
let selectorName = "name"
foo.perform(Selector(selectorName))
仅当您的 foo
类是 NSObject
的子类时才可用
【讨论】:
是的,现在可以了。在回答时, performSelector 被“禁止”。似乎现在 Swift 为每个可能的 performSelector 创建了一个符号(一个静态函数)。我更新我的答案perform
不是关键字。这是正确的还是我误解了什么?
perform
是NSObject
(Swift 3) developer.apple.com/reference/objectivec/nsobject/… 的方法【参考方案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"))
【讨论】:
是项目名,命名空间以上是关于在 Swift 中从字符串调用方法的主要内容,如果未能解决你的问题,请参考以下文章