Como posso lidar com a depreciação de @objc com #selector () no Swift 4?

Estou tentando converter o código-fonte do meu projeto do Swift 3 para o Swift 4. Um aviso que o Xcode está me dando é sobre meus seletores.

Por exemplo, eu adiciono um alvo a um botão usando um seletor regular como este:

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

Este é o aviso que mostra:

Argumento de ‘#selector’ refere-se ao método de instância ‘myAction ()’ em ‘ViewController’ que depende da inferência do atributo ‘@objc’ reprovado no Swift 4

Adicione ‘@objc’ para expor este método de instância ao Objective-C

Agora, apertar Fix na mensagem de erro faz isso para minha function:

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

Eu realmente não quero renomear todas as minhas funções para include a marca @objc e estou assumindo que não é necessário.

Como eu reescrevo o seletor para lidar com a depreciação?


Questão relacionada:

  • O uso da inferência Swift 3 @objc no modo Swift 4 está obsoleto?

A correção está correta – não há nada sobre o seletor que você pode alterar para tornar o método a que ele se refere exposto a Objective-C.

Toda a razão para este aviso, em primeiro lugar, é o resultado do SE-0160 . Antes do Swift 4, os membros compatíveis com Objective-C internal ou superiores de classs @objc NSObject eram inferidos como sendo @objc e, portanto, expostos a Objective-C, permitindo que fossem chamados usando seletores (já que o tempo de execução Obj-C é requerido para para pesquisar a implementação do método para um determinado seletor).

No entanto, no Swift 4, isso não é mais o caso. Apenas declarações muito específicas agora são inferidas como sendo @objc , por exemplo, substituições de methods @objc , implementações de requisitos de protocolo @objc e declarações com atributos que implicam @objc , como @IBOutlet .

A motivação por trás disso, conforme detalhado na proposta vinculada acima , é, em primeiro lugar, evitar que as sobrecargas de método nas classs NSObject do NSObject colidam umas com as outras devido a terem seletores idênticos. Em segundo lugar, ajuda a reduzir o tamanho do binário por não ter que gerar thunks para membros que não precisam ser expostos a Obj-C e, em terceiro lugar, melhora a velocidade de vinculação dinâmica.

Se você quiser expor um membro a Obj-C, você precisa marcá-lo como @objc , por exemplo:

 class ViewController: UIViewController { @IBOutlet weak var button: UIButton! override func viewDidLoad() { super.viewDidLoad() button.addTarget(self, action: #selector(foo), for: .touchUpInside) } @objc func foo() { // ... } } 

(o migrador deve fazer isso automaticamente para você com seletores quando estiver executando com a opção “minimizar inferência” selecionada)

Para expor um grupo de membros a Obj-C, você pode usar uma @objc extension :

 @objc extension ViewController { // both exposed to Obj-C func foo() {} func bar() {} } 

Isto irá expor todos os membros definidos nele para Obj-C, e dar um erro em qualquer membro que não possa ser exposto a Obj-C (a menos que explicitamente marcado como @nonobjc ).

Se você tem uma class onde você precisa que todos os membros compatíveis com Obj-C sejam expostos a Obj-C, você pode marcar a class como @objcMembers :

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

Agora, todos os membros que podem ser inferidos como sendo @objc serão. No entanto, eu não aconselharia fazer isso a menos que você realmente precise de todos os membros expostos à Obj-C, dadas as desvantagens mencionadas acima de ter membros desnecessariamente expostos.

Como documentação oficial da Apple . você precisa usar @objc para chamar seu método de seleção.

Em Objective-C, um seletor é um tipo que se refere ao nome de um método Objective-C. No Swift, os seletores Objective-C são representados pela estrutura Selector e podem ser construídos usando a expressão #selector . Para criar um seletor para um método que pode ser chamado de Objective-C, passe o nome do método, como #selector(MyViewController.tappedButton(sender:)) . Para construir um seletor para o método getter ou setter Objective-C de uma propriedade, passe o nome da propriedade prefixado pelo getter: ou setter: label, como #selector(getter: MyViewController.myButton) .