Swift #selector语法
Swift #selector语法

Swift #selector语法

在使用Swift的过程中,可能会遇到包含 #selectore 的代码:

helpMenu.addItem(withTitle: "访问帮助页面", action: #selector(openHelp), keyEquivalent: "?")

#selector 是 Swift 中用来引用 Objective-C 方法(即“选择器”)的语法,它的作用是告诉系统:当某个事件发生时,应该调用哪个方法。

什么是 Selector?

Selector 是 Objective-C 世界中的一个概念,表示方法的名称,用于动态调用方法。

例如:

@objc func quitApp() {
    NSApp.terminate(nil)
}

可以用 Selector 来引用这个方法:

let quitItem = NSMenuItem(title: "退出", action: #selector(quitApp), keyEquivalent: "q")

系统就知道,当用户点击这个菜单项时,要调用 quitApp() 方法。

语法说明

#selector(方法名)

或者如果方法带参数:

#selector(methodName(_:))

比如:

@objc func showAlert(_ sender: Any?) {
    // ...
}
button.action = #selector(showAlert(_:))

为什么要加 @objc?

因为 Selector 属于 Objective-C 的运行时机制,Swift 方法默认是静态调度的(编译时确定),必须加 @objc 才能暴露给运行时,也才能被 #selector 使用。

@objc func xxx() {} // 可以 selector
func xxx() {} // 编译错误(不是 @objc)

自动传参机制

在#selector()中虽然没有显式传参,但是在调用方法的时候,会自动传入触发该实际的对象。

例如,在NSMenuItem中,action会调用openApp方法:

let openItem = NSMenuItem(title: "打开 App", action: #selector(openApp), keyEquivalent: "o")
openItem.target = self

@objc func openApp() {
    print("打开 App")
    NSApp.activate(ignoringOtherApps: true)
}

在openApp中设置一个open参数,类型为Any:

@objc func openApp(_ open: Any) {
    print("传入的是\(type(of:open))")
    print("打开 App")
    NSApp.activate(ignoringOtherApps: true)
}

当调用openApp方法时,实际上输出的是:

传入的是NSMenuItem
打开 App

这里传递的open参数实际上就是NSMenuItem本身。

因此,可以通过自动传参机制修改调用的对象。

为什么可以传递参数?

因为,在触发action时,macOS会自动尝试传入一个参数(点击的NSMenuItem)。

如果方法是@obj func openApp()(不带参数),只要运行环境能匹配到不带参数的方法,就会调用该方法。

背后的机制在于,Selector是一个函数签名标识:

#selector(openApp)

它最终会被翻译为Objective-C的方法表示:

@selector(openApp)

@selector(openApp:)

这取决于有没有写参数。

在macOS中,当点击NSMenuItem菜单项时,系统会执行:

[target performSelector:action withObject:self]

也就是说它默认会把当前的菜单项(自己)作为参数传入,相当于 Swift 中的:

target.openApp(menuItem)

没有参数

如果写成:

@objc func openApp()

然后调用:

let item = NSMenuItem(title: "打开", action: #selector(openApp), keyEquivalent: "o")
item.target = self

只要系统找到 @objc func openApp() 这个方法,它就能调用。虽然系统本来是想传一个参数的,但这个方法只接受 0 个参数,于是系统会改为调用:

[target openApp] // 无参数版本

macOS 会尽力找最匹配的方法 —— 如果找不到,才会报错;如果能调用 0 参数版本,就默认调用它。

总结

#selector是Swift 中引用 Objective-C 方法的一种语法,常用于按钮、菜单、通知等回调触发,被引用的方法必须加 @objc 修饰。

在SwiftUI中使用闭包处理事件:

Button("退出") {
    NSApp.terminate(nil)
}

而 AppKit / UIKit 中使用 Selector,更接近 Objective-C 风格。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注