我正在尝试将我的项目源代码从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
标记,我假设没有必要.
如何重写选择器来处理弃用?
相关问题:
不推荐在Swift 4模式下使用Swift 3 @objc推理?
Hamish..
152
修复 - 它是正确的 - 您可以更改选择器,以使其引用的方法暴露给Objective-C.
这个警告的全部原因首先是SE-0160的结果.在Swift 4 internal
或更高版本之前,NSObject
继承类的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的所有成员,因为上面提到了让成员不必要地暴露的缺点.
1> Hamish..:
修复 - 它是正确的 - 您可以更改选择器,以使其引用的方法暴露给Objective-C.
这个警告的全部原因首先是SE-0160的结果.在Swift 4 internal
或更高版本之前,NSObject
继承类的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的所有成员,因为上面提到了让成员不必要地暴露的缺点.
男人选择器是一团糟.似乎所有其他快速更新他们都在搞乱它.为什么我们不能只为方法拥有一个纯粹的swift自动完成选择器.使代码看起来如此丑陋.
@Sti所以直接回答"*是否没有纯粹的Swift方式向按钮添加目标?或者使用选择器?*" - 不,没有"纯粹的Swift"方式使用选择器来调度方法.它们依赖于Obj-C运行时以查找方法实现以调用特定的选择器--Swift运行时没有该功能.
我不明白这一点.我没有Objective-C代码.没有纯粹的Swift方式向按钮添加目标吗?或者使用选择器?我不希望我的代码充满这个属性.是因为UIButton来自某些NSObject/Obj-c-thing?
为什么Xcode在将代码转换为最新语法时不会自动执行此操作?
2> Kiran Sarvai..:
作为Apple官方文档.你需要使用@objc来调用你的选择器方法.
在Objective-C中,选择器是一种引用Objective-C方法名称的类型.在Swift中,Objective-C选择器由Selector
结构表示,并且可以使用#selector
表达式构造.要为可以从Objective-C调用的方法创建选择器,请传递方法的名称,例如
#selector(MyViewController.tappedButton(sender:))
.要为属性的Objective-C getter或setter方法构造选择器,请传递以getter:
or setter:
标签为前缀的属性名称,例如
#selector(getter: MyViewController.myButton)
.
3> 小智..:
至于我认为Swift 4.2,您需要做的就是将@IBAction分配给您的方法,您可以避免这种愚蠢的@objc注释
```
let tap = UITapGestureRecognizer(target: self, action: #selector(self.cancel))
@IBAction func cancel()
{
self.dismiss(animated: true, completion: nil)
}