如何通过代码改造规避#selector与@objc的使用?
问题描述
我编写了一个简单测试示例来阐述需求:有时我们需要编写依赖selector的代码,而这类selector要求对应的函数添加@objc修饰。我的目标是通过代码重构,尽可能规避@objc修饰符的使用,更理想的是完全不再使用selector。示例代码如下:
class MyTestAppClass { @objc func test() { print("Hello, world!") } } let test = NSMenuItem(title: "Test", action: #selector(MyTestAppClass.test), keyEquivalent: "t")
如示例所示,NSMenuItem需要传入selector,对应的函数必须添加@objc修饰。我希望优先避免使用selector,若无法实现则至少规避@objc。请问是否存在可行的实现方案?能否将代码改造成更具Swift风格的版本?
可行方案
1. 闭包封装:彻底摆脱selector与@objc
Cocoa的NSMenuItem基于Objective-C的selector机制,但我们可以通过扩展封装闭包回调,让上层代码用Swift原生的闭包处理点击事件,完全不需要@objc或手动管理selector。
实现逻辑是利用关联对象存储闭包,用一个私有的@objc方法作为桥接触发闭包——这个桥接方法是唯一需要@objc的地方,上层代码完全感知不到:
import Cocoa private var ActionClosureAssociatedKey: UInt8 = 0 extension NSMenuItem { // 新增便利构造器,直接接收闭包 convenience init(title: String, keyEquivalent: String, action: @escaping () -> Void) { self.init(title: title, action: #selector(triggerAction), keyEquivalent: keyEquivalent) // 将闭包绑定到NSMenuItem实例 objc_setAssociatedObject(self, &ActionClosureAssociatedKey, action, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } // 私有桥接方法,触发闭包 @objc private func triggerAction() { guard let action = objc_getAssociatedObject(self, &ActionClosureAssociatedKey) as? () -> Void else { return } action() } } // 改造后的使用示例 class MyTestAppClass { func test() { // 完全不需要@objc修饰 print("Hello, world!") } } let testInstance = MyTestAppClass() let testMenuItem = NSMenuItem(title: "Test", keyEquivalent: "t") { testInstance.test() }
这种方式的优势:
MyTestAppClass的方法无需任何Objective-C相关修饰,保持纯Swift风格- 创建
NSMenuItem时直接传入闭包,逻辑更直观,避免了selector字符串匹配的风险 - 闭包可以直接捕获上下文变量,灵活性远超selector
2. 隐式暴露方法:规避手动添加@objc(备选)
如果必须保留selector机制,可以通过让类继承NSObject,让Swift自动将方法暴露给Objective-C,无需手动添加@objc:
class MyTestAppClass: NSObject { // 继承NSObject func test() { // 无需@objc修饰 print("Hello, world!") } } let testMenuItem = NSMenuItem(title: "Test", action: #selector(MyTestAppClass.test), keyEquivalent: "t")
不过这种方式本质上还是依赖Objective-C的selector机制,只是省去了手动写@objc的步骤,风格上不如闭包方案纯粹。
总结
优先选择闭包封装方案,它完全脱离了Objective-C的selector约束,代码更符合Swift的函数式编程风格,同时彻底规避了@objc修饰符的使用,是最理想的改造方式。
内容的提问来源于stack exchange,提问作者user21186802
相关产品推荐
相关产品推荐

