我该如何处理Swift 4中#selector()的@objc推理弃用?

我试图将我的项目的源代码从Swift 3转换为Swift 4. Xcode给我的一个警告是关于我的选择器。

例如,我使用像这样的常规选择器将一个目标添加到按钮:

button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside) 

这是它显示的警告:

“#selector”的参数是指“ViewController”中的实例方法“myAction()”,它依赖于Swift 4中弃用的“@objc”属性推断

添加“@objc”以将此实例方法公开给Objective-C

现在,点击错误消息Fix这对我的功能:

 // before func myAction() { /* ... */ } // after @objc func myAction() { /* ... */ } 

我真的不想重命名所有的函数来包含@objc标记,我假设没有必要。

如何重写选择器来处理弃用?

修复 – 这是正确的 – 没有什么关于可以改变的选择器,以使它所指的方法暴露给Objective-C。

这个警告的全部原因首先是SE-0160的结果。 在Swift 4之前, NSObject继承类的internal或更高级Objective-C兼容成员被推断为@objc ,因此被暴露给Objective-C,因此允许使用选择器来调用它们(因为Obj-C运行时需要按顺序查找给定选择器的方法实现)。

不过在Swift 4中,情况已经不同了。 现在推断只有特定的声明是@objc ,例如覆盖@objc方法, @objc协议要求的实现以及@objc属性的声明,比如@IBOutlet

如上述链接提议中所详述的,其背后的动机首先是防止NSObject继承类中的方法重载由于具有相同的选择器而相互冲突。 其次,它不需要为不需要暴露给Obj-C的成员生成Thunk,有助于减少二进制大小,并且可以提高动态链接的速度。

如果你想将一个成员暴露给Obj-C,你需要将它标记为@objc ,例如:

 class ViewController: UIViewController { @IBOutlet weak var button: UIButton! override func viewDidLoad() { super.viewDidLoad() button.addTarget(self, action: #selector(foo), for: .touchUpInside) } @objc func foo() { // ... } } 

(在选择“最小推论”选项运行时,迁移者应该使用选择器自动执行此操作)

要向Obj-C公开一组成员,可以使用@objc extension

 @objc extension ViewController { // both exposed to Obj-C func foo() {} func bar() {} } 

这将暴露在Obj-C中定义的所有成员,并给任何不能暴露给Obj-C的成员(除非明确标记为@nonobjc )。

如果你有一个需要所有 Obj-C兼容成员暴露给Obj-C的类,你可以把这个类标记为@objcMembers

 @objcMembers class ViewController: UIViewController { // ... } 

现在所有可以被推断为@objc成员都是。 但是我不会建议这样做,除非你真的需要所有暴露给Obj-C的成员,因为上面提到的成员不必要地暴露了成员的缺点。

作为苹果官方文档 。 你需要使用@objc来调用你的Selector方法。

在Objective-C中,选择器是引用Objective-C方法名称的类型。 在Swift中,Objective-C选择器由Selector结构表示,可以使用#selector表达式来构造。 要为可以从Objective-C调用的方法创建选择器,请传递方法的名称,例如#selector(MyViewController.tappedButton(sender:)) 。 要为属性的Objective-C getter或setter方法构造选择器,请传递由getter:setter: label前缀的属性名称,例如#selector(getter: MyViewController.myButton)