134 votes

Comment gérer la dépréciation de l'inférence @objc avec #selector() en Swift 4 ?

J'essaie de convertir le code source de mon projet de Swift 3 à Swift 4. Un avertissement que Xcode me donne concerne mes sélecteurs.

Par exemple, j'ajoute une cible à un bouton à l'aide d'un sélecteur ordinaire comme celui-ci :

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

Voici l'avertissement qu'il affiche :

L'argument de "#selector" fait référence à la méthode d'instance "myAction()" dans "ViewController" qui dépend de l'inférence d'attribut "@objc", obsolète dans Swift 4.

Ajoutez '@objc' pour exposer cette méthode d'instance à l'Objective-C

Maintenant, en frappant Fix sur le message d'erreur fait cela à ma fonction :

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

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

Je n'ai pas vraiment envie de renommer toutes mes fonctions pour y inclure l'élément @objc et je suppose que ce n'est pas nécessaire.

Comment réécrire le sélecteur pour tenir compte de la dépréciation ?


Question connexe :

3 votes

Non, en les marquant comme @objc es sont désormais nécessaires pour les exposer à Obj-C, et donc les utiliser avec des sélecteurs.

3 votes

La partie dépréciée consiste donc à déduire les fonctions d'accès public en tant que @objc ? C'est un peu ennuyeux, mais je rends généralement ces fonctions privées, ce qui m'oblige à les marquer en tant que @objc Quoi qu'il en soit.

2 votes

Véase SE-0160 pour plus d'informations sur ce changement. Une autre solution consiste à marquer votre classe donnée comme @objcMembers afin d'exposer à Obj-C tous les membres compatibles avec Obj-C, mais je ne le conseillerais pas à moins que vous n'ayez réellement besoin que toute votre classe soit exposée.

157voto

Hamish Points 42073

Le correctif est correct - il n'y a rien à changer dans le sélecteur pour que la méthode à laquelle il se réfère soit exposée à l'Objective-C.

La raison d'être de cet avertissement est la suivante SE-0160 . Avant Swift 4, internal ou plus compatible avec l'Objective-C NSObject ont été déduites comme étant des classes @objc et donc exposées à l'Objective-C, ce qui permet de les appeler à l'aide de sélecteurs (l'exécution de l'Obj-C étant nécessaire pour rechercher l'implémentation de la méthode pour un sélecteur donné).

Cependant, dans Swift 4, ce n'est plus le cas. Seules les déclarations très spécifiques sont maintenant déduites comme étant des @objc par exemple, les dérogations aux @objc des méthodes, des mises en œuvre de @objc les exigences du protocole et les déclarations avec des attributs qui impliquent @objc , tels que @IBOutlet .

La motivation de cette démarche, telle qu'elle est détaillée dans la proposition susmentionnée L'objectif de ce programme est tout d'abord d'éviter les surcharges de méthodes dans le domaine de la santé. NSObject les classes qui héritent d'une autre classe n'entrent pas en collision les unes avec les autres parce qu'elles ont des sélecteurs identiques. Deuxièmement, cela permet de réduire la taille des binaires en n'ayant pas à générer de thunks pour les membres qui n'ont pas besoin d'être exposés à Obj-C, et troisièmement, cela améliore la vitesse de l'enchaînement dynamique.

Si vous voulez exposer un membre à Obj-C, vous devez le marquer comme @objc par exemple :

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

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

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

(le migrateur devrait le faire automatiquement pour vous avec les sélecteurs lorsqu'il fonctionne avec l'option "minimiser l'inférence")

Pour exposer un groupe de membres à Obj-C, vous pouvez utiliser un fichier @objc extension :

@objc extension ViewController {

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

Cela exposera à Obj-C tous les membres qui y sont définis et provoquera une erreur pour tous les membres qui ne peuvent pas être exposés à Obj-C (à moins qu'ils ne soient explicitement marqués comme des @nonobjc ).

Si vous avez une classe pour laquelle vous avez besoin de todos pour que les membres compatibles avec Obj-C soient exposés à Obj-C, vous pouvez marquer la classe comme @objcMembers :

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

Maintenant, tous les membres dont on peut déduire qu'ils sont @objc sera. Cependant, je ne vous conseille pas de le faire si vous n'avez pas vraiment ont besoin que tous les membres soient exposés à Obj-C, compte tenu des inconvénients susmentionnés liés à l'exposition inutile de certains membres.

2 votes

Pourquoi Xcode ne le fait-il pas automatiquement lors de la conversion du code à la dernière syntaxe ?

0 votes

@LinusGeffarth Hmm, c'est le cas pour moi - quand je vais dans Edit > Convert > To Current Swift Syntax... et que je passe par le migrateur (avec "minimise inference" sélectionné), tous les sélecteurs qui se réfèrent à des méthodes qui ne sont pas des @objc se faire faire @objc . Essayez peut-être de relancer le migrateur.

0 votes

C'est vrai, j'aurais dû essayer. Je l'ai fait manuellement maintenant. Merci quand même.

16voto

Kiran Sarvaiya Points 844

En Documentation officielle d'Apple . vous devez utiliser @objc pour appeler votre méthode de sélection.

En Objective-C, un sélecteur est un type qui fait référence au nom d'une méthode Objective-C. En Swift, les sélecteurs Objective-C sont représentés par le Selector et peut être construite à l'aide de la structure #selector l'expression. Pour créer un sélecteur pour une méthode qui peut être appelée en Objective-C, passez le nom #selector(MyViewController.tappedButton(sender:)) . Pour construire un sélecteur pour la méthode Objective-C getter ou setter d'une propriété, il faut lui passer le nom de la propriété préfixé par l'attribut getter: o setter: l #selector(getter: MyViewController.myButton) .

0 votes

Tout ce que je comprends, c'est que dans certains cas, je dois ajouter @objc au début d'un func, que je l'appelle ou non depuis l'Objective-C. Je suis en train d'apprendre Swift après avoir utilisé Obj-C pendant des années.

11voto

Logan Sease Points 99

Depuis, je crois, Swift 4.2, tout ce que vous avez à faire, c'est d'assigner @IBAction à votre méthode et d'éviter les @objc annotation.

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

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

4 votes

Cela indiquera également au constructeur d'interface que cette fonction peut être connectée, ce qui n'est peut-être pas ce que vous voulez. Cela fait également la même chose que @objc.

2voto

S1LENT WARRIOR Points 412

Comme cela a déjà été mentionné dans d'autres réponses, il n'y a aucun moyen d'éviter la @objc pour les sélecteurs.

Mais l'avertissement mentionné dans l'OP peut être réduit au silence en prenant les mesures suivantes :

  1. Aller à Paramètres de construction
  2. Recherche d'un mot-clé @objc
  3. Fixer la valeur de Interface @objc Swift 3 a Off

La capture d'écran ci-dessous illustre les étapes mentionnées ci-dessus :

Silencing the warning "Swift 3 @objc interface"

J'espère que cela vous aidera

0 votes

Comment cela affecte-t-il votre projet dans son ensemble ?

1 votes

Qu'en est-il de la taille des fichiers *.ipa ou *.xarchive et du temps de construction ?

0 votes

@ZonilyJame je n'ai pas remarqué le temps de construction, mais il n'y avait pas d'effet sur la taille de l'IPA

2voto

Renjish C Points 16

Si vous avez besoin de membres objectifs c dans votre contrôleur de vue, ajoutez simplement @objcMembres en haut du contrôleur de vue. Vous pouvez éviter cela en ajoutant IBAction dans votre code.

@IBAction func buttonAction() {

}

Veillez à connecter cette prise dans le storyboard.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X