134

我正在尝试将项目的源代码从 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标记,并且我认为这不是必需的。

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


相关问题:

4

5 回答 5

156

修复它是正确的——您可以更改选择器以使其引用的方法暴露给 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 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,否则我不建议这样做,因为上面提到的让成员不必要地暴露的缺点。

于 2017-06-06T13:29:56.337 回答
16

作为Apple 官方文档。您需要使用 @objc 来调用您的 Selector 方法。

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

于 2017-06-06T13:34:12.367 回答
11

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

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

@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}
于 2018-09-26T22:01:08.040 回答
2

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

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

  1. 转到构建设置
  2. 搜索关键字@objc
  3. 将Swift 3 @objc 接口的值设置为Off

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

使警告静音

希望这可以帮助

于 2018-11-23T00:25:21.673 回答
2

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

@IBAction func buttonAction() {

}

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

于 2019-02-07T06:56:26.160 回答