Question @selector () dans Swift?


J'essaie de créer un NSTimer dans Swift mais j'ai des problèmes.

NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)

test() est une fonction dans la même classe.


Je reçois une erreur dans l'éditeur:

Impossible de trouver une surcharge pour 'init' qui accepte le fourni   arguments

Quand je change selector: test() à selector: nil l'erreur disparaît.

J'ai essayé:

  • selector: test()
  • selector: test
  • selector: Selector(test())

Mais rien ne marche et je ne trouve pas de solution dans les références.


584
2018-06-03 05:21


origine


Réponses:


Rapide se n'utilise pas de sélecteurs - plusieurs modèles de conception qui utilisent des sélecteurs dans Objective-C fonctionnent différemment dans Swift. (Par exemple, utilisez le chaînage facultatif sur les types de protocole ou is/as tests au lieu de respondsToSelector:, et utilisez des fermetures partout où vous le pouvez au lieu de performSelector: pour un meilleur type / sécurité de la mémoire.)

Mais il existe encore un certain nombre d'API importantes basées sur ObjC qui utilisent des sélecteurs, notamment des temporisations et le modèle cible / action. Swift fournit le Selector tapez pour travailler avec ceux-ci. (Swift l'utilise automatiquement à la place d'ObjC SEL type.)

Dans Swift 2.2 (Xcode 7.3) et plus tard (y compris Swift 3 / Xcode 8 et Swift 4 / Xcode 9):

Vous pouvez construire un Selector à partir d'un type de fonction Swift en utilisant le #selector expression.

let timer = Timer(timeInterval: 1, target: object,
                  selector: #selector(MyClass.test),
                  userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
                 for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
             with: button, with: otherButton)

La bonne chose à propos de cette approche? Une référence de fonction est vérifiée par le compilateur Swift, vous pouvez donc utiliser le #selector expression uniquement avec les paires classe / méthode qui existent réellement et qui peuvent être utilisées comme sélecteurs (voir "Disponibilité des sélecteurs" ci-dessous). Vous êtes également libre de rendre votre référence de fonction aussi spécifique que nécessaire, selon les règles Swift 2.2+ pour la dénomination de type fonction.

(Ceci est en fait une amélioration par rapport à ObjC @selector() directive, parce que le compilateur -Wundeclared-selector check vérifie seulement que le sélecteur nommé existe. La référence de la fonction Swift que vous passez à #selector vérifie l'existence, l'appartenance à une classe et la signature de type.)

Il y a quelques mises en garde supplémentaires pour les références de fonctions que vous transmettez au #selector expression:

  • Plusieurs fonctions avec le même nom de base peuvent être différenciées par leurs étiquettes de paramètres en utilisant les syntaxe pour les références de fonction (par exemple. insertSubview(_:at:) contre insertSubview(_:aboveSubview:)). Mais si une fonction n'a pas de paramètres, la seule façon de la désamorcer est d'utiliser un as cast avec la signature de type de la fonction (par ex. foo as () -> () contre foo(_:)).
  • Il y a une syntaxe spéciale pour les paires getter / setter de propriété dans Swift 3.0+. Par exemple, compte tenu d'un var foo: Int, vous pouvez utiliser #selector(getter: MyClass.foo) ou #selector(setter: MyClass.foo).

Notes générales:

Cas où #selector ne fonctionne pas, et nommer: Parfois, vous n'avez pas de référence de fonction pour faire un sélecteur avec (par exemple, avec des méthodes enregistrées dynamiquement dans le runtime ObjC). Dans ce cas, vous pouvez construire un Selector à partir d'une chaîne: par ex. Selector("dynamicMethod:") - bien que vous perdiez la vérification de la validité du compilateur. Quand vous faites cela, vous devez suivre les règles de nommage d'ObjC, y compris les deux-points (:) pour chaque paramètre.

Disponibilité du sélecteur:La méthode référencée par le sélecteur doit être exposée à l'environnement d'exécution ObjC. Dans Swift 4, toute méthode exposée à ObjC doit avoir sa déclaration préfacée au @objc attribut. (Dans les versions précédentes, vous avez cet attribut gratuitement dans certains cas, mais maintenant vous devez le déclarer explicitement.)

Souviens toi que private les symboles ne sont pas non plus exposés à l'exécution - votre méthode doit avoir au moins internal visibilité.

Chemins clés: Ceux-ci sont liés à mais pas tout à fait la même chose que les sélecteurs. Il y a une syntaxe spéciale pour ceux-ci dans Swift 3, par exemple: chris.valueForKeyPath(#keyPath(Person.friends.firstName)). Voir SE-0062 pour plus de détails. Et encore plus KeyPath des trucs dans Swift 4, donc assurez-vous que vous utilisez la bonne API basée sur KeyPath au lieu de sélecteurs, le cas échéant.

Vous pouvez en savoir plus sur les sélecteurs sous Interaction avec les API Objective-C dans Utiliser Swift avec du cacao et Objective-C.

Remarque: Avant Swift 2.2, Selector conforme à StringLiteralConvertible, donc vous pourriez trouver l'ancien code où les chaînes nues sont transmises aux API qui acceptent les sélecteurs. Vous devez exécuter "Convert to Current Swift Syntax" dans Xcode pour obtenir ceux qui utilisent #selector.


865
2018-06-03 05:27



Voici un exemple rapide sur la façon d'utiliser le Selector classe sur Swift:

override func viewDidLoad() {
    super.viewDidLoad()

    var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
    self.navigationItem.rightBarButtonItem = rightButton
}

func method() {
    // Something cool here   
}

Notez que si la méthode transmise en tant que chaîne ne fonctionne pas, elle échouera à l'exécution, ne compilera pas le temps et provoquera un crash de votre application. Faites attention


78
2018-06-03 05:31



En outre, si votre classe (Swift) ne descend pas d'une classe Objective-C, vous devez avoir deux points à la fin de la chaîne de nom de la méthode cible et vous devez utiliser la propriété @objc avec votre méthode cible, par exemple.

var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))

@objc func method() {
    // Something cool here   
} 

sinon, vous obtiendrez une erreur "Sélecteur non reconnu" lors de l'exécution.


42
2018-06-24 15:23



Pour les futurs lecteurs, j'ai trouvé que j'avais un problème et obtenais un unrecognised selector sent to instance erreur qui a été causée en marquant la cible func comme privé.

le func  DOIT être publiquement visible pour être appelé par un objet avec une référence à un sélecteur.


22
2018-04-10 18:08



Mise à jour Swift 2.2+ et Swift 3

Utilisez le nouveau #selector expression, ce qui élimine le besoin d'utiliser des littéraux de chaîne rendant l'utilisation moins sujette aux erreurs. Pour référence:

Selector("keyboardDidHide:")

devient

#selector(keyboardDidHide(_:))

Voir également: Proposition d'évolution rapide

Note (Swift 4.0): 

Si vous utilisez #selectorvous auriez besoin de marquer la fonction comme @objc

Exemple:

@objc func something(_ sender: UIButton)


22
2018-04-06 16:06



Juste au cas où quelqu'un d'autre aurait le même problème avec NSTimer où aucune des autres réponses n'a résolu le problème, il est important de le mentionner, si vous utilisez une classe qui n'hérite pas de NSObject directement ou dans la hiérarchie ( par exemple les fichiers swift créés manuellement), aucune des autres réponses ne fonctionnera même si elle est spécifiée comme suit:

let timer = NSTimer(timeInterval: 1, target: self, selector: "test", 
                    userInfo: nil, repeats: false)
func test () {}

Sans changer quoi que ce soit d'autre que de simplement faire hériter la classe de NSObject, j'ai arrêté d'obtenir l'erreur "Unrecognized selector" et j'ai réussi à faire fonctionner ma logique comme prévu.


19
2017-07-13 05:31



Swift 4.0

vous créez le sélecteur comme ci-dessous.

1.add l'événement à un bouton comme:

button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)

et la fonction sera comme ci-dessous:

@objc func clickedButton(sender: AnyObject) {

}

15
2017-10-15 07:27