如何在 Swift 4 中使用 #selector() 处理 @objc 推理弃用?
How can I deal with @objc inference deprecation with #selector() in Swift 4?
我正在尝试将项目的源代码从 Swift 3 转换为 Swift 4。Xcode 给我的一个警告是关于我的选择器的。
例如,我使用这样的常规选择器将目标添加到按钮:
button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)
这是它显示的警告:
Argument of '#selector' refers to instance method 'myAction()' in 'ViewController' that depends on '@objc' attribute inference deprecated in Swift 4
Add '@objc' to expose this instance method to Objective-C
现在,在错误消息上点击 Fix
会对我的函数执行此操作:
// before
func myAction() { /* ... */ }
// after
@objc func myAction() { /* ... */ }
我真的不想重命名我的所有函数以包含 @objc
标记,我认为这没有必要。
如何重写选择器以处理弃用问题?
相关问题:
- The use of Swift 3 @objc inference in Swift 4 mode is deprecated?
修复是正确的 – 您无法更改选择器以使其引用的方法暴露给 Objective-C。
首先出现此警告的全部原因是 SE-0160 的结果。在 Swift 4 之前,NSObject
的 internal
或更高 Objective-C 兼容成员继承 classes 被推断为 @objc
,因此暴露于 Objective-C,因此允许使用选择器调用它们(因为需要 Obj-C 运行时才能查找给定选择器的方法实现)。
然而在Swift4中,情况不再如此。现在只有非常具体的声明才被推断为 @objc
,例如,@objc
方法的覆盖、@objc
协议要求的实现以及具有暗示 @objc
的属性的声明,例如@IBOutlet
.
这背后的动机,详述 in the above linked proposal,首先是为了防止 NSObject
继承 class 的方法重载由于具有相同的选择器而相互冲突。其次,它不必为不需要暴露给 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)
}
<b>@objc</b> func foo() {
// ...
}
}
(当 运行 选择了 "minimise inference" 选项时,迁移器应该会自动为您使用选择器执行此操作)
要向 Obj-C 公开一组成员,您可以使用 @objc extension
:
@objc extension ViewController {
// both exposed to Obj-C
func foo() {}
func bar() {}
}
这会将其中定义的所有成员暴露给 Obj-C,并对任何不能暴露给 Obj-C 的成员给出错误(除非明确标记为 @nonobjc
)。
如果你有一个class需要所有 Obj-C兼容成员暴露给Obj-C,你可以标记class作为 @objcMembers
:
@objcMembers
class ViewController: UIViewController {
// ...
}
现在,所有可以推断为 @objc
的成员都将是。但是,我不建议这样做,除非你真的需要所有成员都暴露给 Obj-C,考虑到上面提到的让成员不必要地暴露的缺点。
如Apple Official Documentation。您需要使用@objc 来调用您的选择器方法。
In Objective-C, a selector is a type that refers to the name of an
Objective-C method. In Swift, Objective-C selectors are represented by
the Selector
structure, and can be constructed using the #selector
expression. To create a selector for a method that can be called from
Objective-C, pass the name of the method, such as
#selector(MyViewController.tappedButton(sender:))
. To construct a selector for a property’s Objective-C getter or setter method, pass
the property name prefixed by the getter:
or setter:
label, such as
#selector(getter: MyViewController.myButton)
.
从 Swift 4.2 开始,您需要做的就是将 @IBAction
分配给您的方法并避免使用 @objc
注释。
let tap = UITapGestureRecognizer(target: self, action: #selector(self.cancel))
@IBAction func cancel()
{
self.dismiss(animated: true, completion: nil)
}
正如其他答案中已经提到的,选择器无法避免使用 @objc
注释。
但是可以通过以下步骤消除 OP 中提到的警告:
- 转到构建设置
- 搜索关键字 @objc
- 将Swift 3 @objc interface的值设为
Off
下面是说明上述步骤的屏幕截图:
希望对您有所帮助
如果您的视图控制器中需要 objective c 成员,只需在视图控制器顶部添加 @objcMembers。您可以通过在代码中添加 IBAction 来避免这种情况。
@IBAction func buttonAction() {
}
确保在故事板中连接此插座。
我正在尝试将项目的源代码从 Swift 3 转换为 Swift 4。Xcode 给我的一个警告是关于我的选择器的。
例如,我使用这样的常规选择器将目标添加到按钮:
button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)
这是它显示的警告:
Argument of '#selector' refers to instance method 'myAction()' in 'ViewController' that depends on '@objc' attribute inference deprecated in Swift 4
Add '@objc' to expose this instance method to Objective-C
现在,在错误消息上点击 Fix
会对我的函数执行此操作:
// before
func myAction() { /* ... */ }
// after
@objc func myAction() { /* ... */ }
我真的不想重命名我的所有函数以包含 @objc
标记,我认为这没有必要。
如何重写选择器以处理弃用问题?
相关问题:
- The use of Swift 3 @objc inference in Swift 4 mode is deprecated?
修复是正确的 – 您无法更改选择器以使其引用的方法暴露给 Objective-C。
首先出现此警告的全部原因是 SE-0160 的结果。在 Swift 4 之前,NSObject
的 internal
或更高 Objective-C 兼容成员继承 classes 被推断为 @objc
,因此暴露于 Objective-C,因此允许使用选择器调用它们(因为需要 Obj-C 运行时才能查找给定选择器的方法实现)。
然而在Swift4中,情况不再如此。现在只有非常具体的声明才被推断为 @objc
,例如,@objc
方法的覆盖、@objc
协议要求的实现以及具有暗示 @objc
的属性的声明,例如@IBOutlet
.
这背后的动机,详述 in the above linked proposal,首先是为了防止 NSObject
继承 class 的方法重载由于具有相同的选择器而相互冲突。其次,它不必为不需要暴露给 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)
}
<b>@objc</b> func foo() {
// ...
}
}
(当 运行 选择了 "minimise inference" 选项时,迁移器应该会自动为您使用选择器执行此操作)
要向 Obj-C 公开一组成员,您可以使用 @objc extension
:
@objc extension ViewController {
// both exposed to Obj-C
func foo() {}
func bar() {}
}
这会将其中定义的所有成员暴露给 Obj-C,并对任何不能暴露给 Obj-C 的成员给出错误(除非明确标记为 @nonobjc
)。
如果你有一个class需要所有 Obj-C兼容成员暴露给Obj-C,你可以标记class作为 @objcMembers
:
@objcMembers
class ViewController: UIViewController {
// ...
}
现在,所有可以推断为 @objc
的成员都将是。但是,我不建议这样做,除非你真的需要所有成员都暴露给 Obj-C,考虑到上面提到的让成员不必要地暴露的缺点。
如Apple Official Documentation。您需要使用@objc 来调用您的选择器方法。
In Objective-C, a selector is a type that refers to the name of an Objective-C method. In Swift, Objective-C selectors are represented by the
Selector
structure, and can be constructed using the#selector
expression. To create a selector for a method that can be called from Objective-C, pass the name of the method, such as#selector(MyViewController.tappedButton(sender:))
. To construct a selector for a property’s Objective-C getter or setter method, pass the property name prefixed by thegetter:
orsetter:
label, such as#selector(getter: MyViewController.myButton)
.
从 Swift 4.2 开始,您需要做的就是将 @IBAction
分配给您的方法并避免使用 @objc
注释。
let tap = UITapGestureRecognizer(target: self, action: #selector(self.cancel))
@IBAction func cancel()
{
self.dismiss(animated: true, completion: nil)
}
正如其他答案中已经提到的,选择器无法避免使用 @objc
注释。
但是可以通过以下步骤消除 OP 中提到的警告:
- 转到构建设置
- 搜索关键字 @objc
- 将Swift 3 @objc interface的值设为
Off
下面是说明上述步骤的屏幕截图:
希望对您有所帮助
如果您的视图控制器中需要 objective c 成员,只需在视图控制器顶部添加 @objcMembers。您可以通过在代码中添加 IBAction 来避免这种情况。
@IBAction func buttonAction() {
}
确保在故事板中连接此插座。