我该如何处理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)
。