Swift/Objective-C反射:如何将Foo.bar转为-[Foo bar]格式字符串?
当然可以实现!针对你在单元测试里的这个临时需求,我们可以借助Objective-C运行时的能力轻松搞定,完全能作为重大重构前的过渡方案。
核心思路
因为你的Foo类继承自NSObject,天然具备和Objective-C互操作的能力,而Objective-C运行时提供了直接将方法选择器转换成你要的-[Foo bar]格式字符串的API,正好能满足需求。
具体实现步骤
1. 确保方法暴露给Objective-C
Swift默认不会自动把NSObject子类的方法暴露给Objective-C,所以需要给bar()方法加上@objc标记(如果有多个方法,也可以给整个类加@objcMembers批量处理):
import Foundation class Foo : NSObject { @objc func bar() -> Void { // third-party code } }
2. 实现你的字符串化函数
我们可以直接用Objective-C运行时的NSStringFromSelector函数,它能完美输出你要的格式。这里写一个符合你要求的函数:
func MyStringifyingFunction(_ selector: Selector) -> String { return NSStringFromSelector(selector) }
3. 调用方式
使用Swift的#selector语法传递方法的选择器即可:
let result = MyStringifyingFunction(#selector(Foo.bar)) print(result) // 输出 "-[Foo bar]"
进阶:直接传入方法引用Foo.bar
如果确实想直接传入Foo.bar这种方法引用而不是#selector,我们可以写一个泛型函数来做转换(适合临时场景,正式重构更推荐上面的方案):
import ObjectiveC func MyStringifyingFunction<T: NSObject>(_ method: @escaping (T) -> () -> Void) -> String? { // 把方法引用转换成IMP(方法实现指针) guard let imp = method as? IMP else { return nil } // 遍历类的所有实例方法,找到匹配IMP的选择器 var methodCount: UInt32 = 0 let methodList = class_copyMethodList(T.self, &methodCount) defer { free(methodList) } for i in 0..<Int(methodCount) { guard let method = methodList?[i] else { continue } if method_getImplementation(method) == imp { let selector = method_getName(method) return NSStringFromSelector(selector) } } return nil }
调用时直接传方法引用即可:
if let result = MyStringifyingFunction(Foo.bar) { print(result) // 输出 "-[Foo bar]" }
注意:第二种方式因为依赖匹配方法实现指针,存在极小概率的冲突(比如多个方法共享同一个实现),但作为单元测试的临时方案完全够用。正式重构时,更推荐第一种
#selector的方式,逻辑更清晰、可靠性更高。
内容的提问来源于stack exchange,提问作者pyrosphere
相关产品推荐
相关产品推荐

