ChatGPT解决这个技术问题 Extra ChatGPT

如何在 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 标记,我认为这不是必需的。

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

相关问题:

在 Swift 4 模式下使用 Swift 3 @objc 推理已被弃用?

不,现在需要将它们标记为 @objc is 以便将它们公开给 Obj-C,并因此与选择器一起使用。
所以不推荐使用的部分是将公共访问功能推断为 @objc?这有点烦人,但我通常将这些函数设为私有,无论如何都要求我将其标记为 @objc
有关更改的详细信息,请参阅 SE-0160。另一种选择是将给定的类标记为 @objcMembers 以便将所有与 Obj-C 兼容的成员公开给 Obj-C,但我不建议您这样做,除非您确实需要公开整个类。
@LinusGeffarth 正如提案中所说,它可能会不必要地增加二进制文件的大小,并且动态链接需要更长的时间。我真的不认为对于从 Obj-C 中使用的特定事物特别意味着增加的清晰度来说太麻烦了。
试过了,不行。

R
R. Rincón

修复它是正确的——您可以更改选择器以使其引用的方法暴露给 Objective-C 的任何内容。

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

但是在 Swift 4 中,情况不再如此。现在仅将非常具体的声明推断为 @objc,例如,覆盖 @objc 方法、@objc 协议要求的实现以及具有隐含 @objc 的属性的声明,例如 @IBOutlet

如详细的 in the above linked proposal 所述,其背后的动机首先是防止 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,因为上面提到的让成员不必要地暴露的缺点。


为什么 Xcode 在将代码转换为最新语法时不会自动执行此操作?
我想知道,@IBAction 是否也会自动@objc?直到现在情况并非如此,但这是合乎逻辑的。编辑:nvm,提案明确指出 @IBAction 足以使方法成为 @objc
我不明白这些。我从来没有Objective-C代码。是否没有纯粹的 Swift 方式将目标添加到按钮?还是使用选择器?我不希望我的代码充满这个属性。是因为 UIButton 是从一些 NSObject/Obj-c-thing 派生的吗?
@Sti 所以直接回答“没有纯粹的 Swift 方式向按钮添加目标吗?还是使用选择器?” – 不,没有“纯 Swift”方式使用选择器来调度方法。他们依靠 Obj-C 运行时来查找方法实现以调用特定的选择器——Swift 运行时没有这种能力。
男人选择器是一团糟。似乎他们正在搞乱它的所有其他快速更新。为什么我们不能只为方法提供一个纯粹的快速自动完成选择器。让代码看起来很丑。
H
Hamish

作为Apple Official Documentation。您需要使用@objc 来调用您的选择器方法。

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


我从中了解到的是,在某些情况下,无论我是否打算从 Objective-C 调用它,我都需要将 @objc 添加到 func 的开头。我刚刚学习 Swift 已经使用了多年的 Obj-C
r
ricardopereira

截至目前,我认为 Swift 4.2,您需要做的就是将 @IBAction 分配给您的方法并避免 @objc 注释。

let tap  =  UITapGestureRecognizer(target: self, action: #selector(self.cancel))

@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}

这也会告诉界面生成器这个功能可以连接,这可能不是你想要的。它也与@objc 做同样的事情。
S
S1LENT WARRIOR

正如其他答案中已经提到的,没有办法避免选择器的 @objc 注释。

但是可以通过以下步骤消除 OP 中提到的警告:

转到 Build Settings 搜索关键字 @objc 将 Swift 3 @objc 接口的值设置为 Off

下面是说明上述步骤的屏幕截图:

https://i.stack.imgur.com/QNTKm.png

希望这可以帮助


从更大的角度来看,这将如何影响您的项目?
*.ipa 或 *.xarchive 的大小和构建时间如何?
@ZonilyJame 我没有注意到构建时间,但对 IPA 大小没有影响
如果您按照 Apple 指南使用 swift 4.0+ 版本,则这不是您可以使用的推荐值。检查此help.apple.com/xcode/mac/current/#/deve838b19a1
您可以通过在 Xcode 11 中将值设置为 Default 来更新此答案。在项目和目标上设置值以完全删除警告非常重要。
S
S1LENT WARRIOR

如果您在视图控制器中需要目标 c 成员,只需在视图控制器顶部添加 @objcMembers。您可以通过在代码中添加 IBAction 来避免这种情况。

@IBAction func buttonAction() {

}

确保在情节提要中连接此插座。