什么是 Swift 中的 @selector() 以及为什么需要它?
如果您曾经使用过 Objective-C 或遇到过遗留代码,那么您一定听说过神奇的构造 @selector()。在 Swift 中,它转变为 #selector,但本质保持不变:它是 Swift 静态类型与 Objective-C 运行时动态特性之间的桥梁。
想象一下,您需要将方法的引用作为参数传递。例如,用于处理按钮点击或订阅通知。在 Swift 中,我们习惯使用闭包,但 Apple 的库(UIKit、AppKit、Foundation)仍然依赖老式的选择器机制。正是在这里,Swift 中的 @selector() 成为不可或缺的工具。
在本指南中,我们将介绍:
- 如何在现代 Swift 中正确声明选择器
- 典型错误以及如何避免它们
- 来自 iOS 开发的实际示例
- 何时使用
#selector,何时使用替代方案
从 @selector() 到 #selector:语法的演变
在 Objective-C 中,选择器看起来像这样:
[@selector(buttonTapped:)]在 Swift 2.0 中,出现了 #selector 构造,并成为标准。现在看起来像这样:
#selector(MyClass.buttonTapped(_:))关键区别:Swift 中的 #selector 由编译器检查。如果您拼错了方法名或传递了错误的签名,编译器会报错。与 Objective-C 相比,这是一个巨大的优势,在 Objective-C 中,选择器只是一个字符串,错误只会在运行时出现。
用于 #selector 的方法声明规则
传递给 #selector 的方法必须:
- 可从 Objective-C 访问 — 使用
@objc属性标记 - 不是私有的(或标记为
@objc private) - 签名兼容 调用方所期望的签名
正确声明的示例:
class ViewController: UIViewController { @objc func buttonTapped(_ sender: UIButton) { print("按钮已点击!") } override func viewDidLoad() { super.viewDidLoad() let button = UIButton(type: .system) button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside) }}注意方法前的 @objc。没有它,编译器将不允许使用 #selector。
@selector() Swift:实际示例
1. Target-Action:按钮和手势
使用 #selector 最常见的场景是将操作绑定到 UI 元素:
class ProfileViewController: UIViewController { @objc private func avatarTapped(_ gesture: UITapGestureRecognizer) { print("头像被点击") } @objc private func settingsButtonPressed() { print("打开设置") } override func viewDidLoad() { super.viewDidLoad() // 手势 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(avatarTapped(_:))) avatarImageView.addGestureRecognizer(tapGesture) // 按钮 settingsButton.addTarget(self, action: #selector(settingsButtonPressed), for: .touchUpInside) }}请记住:如果方法接受参数(例如 UITapGestureRecognizer),您必须在选择器签名中指定 _:#selector(avatarTapped(_:))。
2. PerformSelector 和延迟调用
有时需要动态调用方法,尤其是在使用 KVO 或定时器时:
class TimerManager { @objc func fireTimer() {