You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:17:57