如何在 Swift 4 中使用 #selector() 处理 @objc 推理弃用? [英] How can I deal with @objc inference deprecation with #selector() in Swift 4?

查看:35
本文介绍了如何在 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 会影响我的函数:

//之前func myAction() {/* ... */}//后@objc func myAction() {/* ... */}

我真的不想重命名我的所有函数以包含 @objc 标记,我认为这不是必需的.

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


相关问题:

解决方案

修复 - 它是正确的 - 没有关于选择器的任何内容,您可以更改以使其引用的方法暴露给 Objective-C.

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

但是在 Swift 4 中,情况不再如此.现在只有非常具体的声明被推断为 @objc,例如,@objc 方法的覆盖,@objc 协议要求和声明的实现带有隐含 @objc 的属性,例如 @IBOutlet.

这背后的动机,详见中上面链接的提议,首先是为了防止NSObject继承类中的方法重载由于具有相同的选择器而相互冲突.其次,它不必为不需要暴露于 Obj-C 的成员生成 thunk,从而有助于减少二进制文件的大小,第三,提高了动态链接的速度.

如果要将成员暴露给Obj-C,则需要将其标记为@objc,例如:

class ViewController: UIViewController {@IBOutlet 弱变量按钮:UIButton!覆盖 func viewDidLoad() {super.viewDidLoad()button.addTarget(self, action: #selector(foo), for: .touchUpInside)}@objc func foo() {//...}}

(在选择最小化推理"选项的情况下运行时,迁移器应该使用选择器自动为您执行此操作)

要将一组成员暴露给 Obj-C,您可以使用 @objc 扩展:

@objc 扩展 ViewController {//都暴露给 Obj-C函数 foo() {}功能栏(){}}

这会将其中定义的所有成员暴露给 Obj-C,并对任何不能暴露给 Obj-C 的成员给出错误(除非明确标记为 @nonobjc).

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

@objcMembers类视图控制器:UIViewController {//...}

现在,所有可以推断为 @objc 的成员都将是.但是,我不建议这样做,除非您真的需要所有成员都暴露于 Obj-C,考虑到上述成员不必要地暴露的缺点.

I'm trying to convert my project's source code from Swift 3 to Swift 4. One warning Xcode is giving me is about my selectors.

For instance, I add a target to a button using a regular selector like this:

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

This is the warning it shows:

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

Now, hitting Fix on the error message does this to my function:

// before
func myAction() { /* ... */ }

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

I don't really want to rename all of my functions to include the @objc mark and I'm assuming that's not necessary.

How do I rewrite the selector to deal with the deprecation?


Related question:

解决方案

The fix-it is correct – there's nothing about the selector you can change in order to make the method it refers to exposed to Objective-C.

The whole reason for this warning in the first place is the result of SE-0160. Prior to Swift 4, internal or higher Objective-C compatible members of NSObject inheriting classes were inferred to be @objc and therefore exposed to Objective-C, therefore allowing them to be called using selectors (as the Obj-C runtime is required in order to lookup the method implementation for a given selector).

However in Swift 4, this is no longer the case. Only very specific declarations are now inferred to be @objc, for example, overrides of @objc methods, implementations of @objc protocol requirements and declarations with attributes that imply @objc, such as @IBOutlet.

The motivation behind this, as detailed in the above linked proposal, is firstly to prevent method overloads in NSObject inheriting classes from colliding with each other due to having identical selectors. Secondly, it helps reduce the binary size by not having to generate thunks for members that don't need to be exposed to Obj-C, and thirdly improves the speed of dynamic linking.

If you want to expose a member to Obj-C, you need to mark it as @objc, for example:

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        button.addTarget(self, action: #selector(foo), for: .touchUpInside)
    }

    @objc func foo() {
       // ... 
    }
}

(the migrator should do this automatically for you with selectors when running with the "minimise inference" option selected)

To expose a group of members to Obj-C, you can use an @objc extension:

@objc extension ViewController {

    // both exposed to Obj-C
    func foo() {}
    func bar() {}
}

This will expose all the members defined in it to Obj-C, and give an error on any members that cannot be exposed to Obj-C (unless explicitly marked as @nonobjc).

If you have a class where you need all Obj-C compatible members to be exposed to Obj-C, you can mark the class as @objcMembers:

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

Now, all members that can be inferred to be @objc will be. However, I wouldn't advise doing this unless you really need all members exposed to Obj-C, given the above mentioned downsides of having members unnecessarily exposed.

这篇关于如何在 Swift 4 中使用 #selector() 处理 @objc 推理弃用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆