web-dev-qa-db-fra.com

Quel est l'équivalent Swift de respondsToSelector?

J'ai cherché sur Google mais je n'ai pas pu trouver ce qu'est _ l'équivalent de Swift et respondsToSelector:.

C’est la seule chose que j’ai pu trouver ( Alternative Swift à respondsToSelector: ) mais n’est pas très pertinente dans mon cas car elle vérifie l’existence du délégué, je n’ai pas de délégué, je veux seulement vérifier si une nouvelle API existe ou non lors de son exécution sur le périphérique et si ce n’est pas le cas, revenir à une version antérieure de l’API.

188
Gruntcakes

Comme mentionné, dans Swift , vous pouvez généralement obtenir ce dont vous avez besoin avec l'opérateur de désencapsulation facultatif ?. Cela vous permet d'appeler une méthode sur un objet si et seulement si l'objet existe (pas nil) et que la méthode est implémentée.

Dans le cas où vous avez encore besoin de respondsToSelector:, il est toujours présent dans le protocole NSObject.

Si vous appelez respondsToSelector: sur un type Obj-C dans Swift, cela fonctionnera comme vous le souhaiteriez. Si vous l'utilisez dans votre propre classe Swift, vous devez vous assurer que votre classe dérive de NSObject.

Voici un exemple de classe Swift que vous pouvez vérifier si elle répond à un sélecteur:

class Worker : NSObject
{
    func work() { }
    func eat(food: AnyObject) { }
    func sleep(hours: Int, minutes: Int) { }
}

let worker = Worker()

let canWork = worker.respondsToSelector(Selector("work"))   // true
let canEat = worker.respondsToSelector(Selector("eat:"))    // true
let canSleep = worker.respondsToSelector(Selector("sleep:minutes:"))    // true
let canQuit = worker.respondsToSelector(Selector("quit"))   // false

Il est important de ne pas omettre les noms de paramètres. Dans cet exemple, Selector("sleep::") est pas identique à Selector("sleep:minutes:").

161
Erik

Il n'y a pas de réel remplacement Swift.

Vous pouvez vérifier de la manière suivante:

someObject.someMethod?()

Ceci appelle la méthode someMethod uniquement si elle est définie sur l'objet someObject, mais vous ne pouvez l'utiliser que pour les protocoles @objc ayant déclaré la méthode optional.

Swift est intrinsèquement un langage sûr, donc chaque fois que vous appelez une méthode, Swift doit savoir que la méthode existe. Aucune vérification d'exécution n'est possible. Vous ne pouvez pas simplement appeler des méthodes aléatoires sur des objets aléatoires.

Même dans Obj-C, vous devriez éviter de telles choses lorsque cela est possible car cela ne fonctionne pas bien avec ARC (ARC déclenche alors des avertissements pour performSelector:).

Toutefois, lorsque vous recherchez les API disponibles, vous pouvez toujours utiliser respondsToSelector:, même si vous utilisez Swift, si vous utilisez NSObject instances:

@interface TestA : NSObject

- (void)someMethod;

@end

@implementation TestA

//this triggers a warning

@end   


var a = TestA()

if a.respondsToSelector("someMethod") {
   a.someMethod()
}
59
Sulthan

Mise à jour du 20 mars 2017 pour la syntaxe Swift 3:

Si vous ne voulez pas que la méthode optionnelle existe, appelez simplement delegate?.optionalMethod?()

Sinon, utiliser guard est probablement la meilleure approche:

weak var delegate: SomeDelegateWithOptionals?

func someMethod() {
    guard let method = delegate?.optionalMethod else {
        // optional not implemented
        alternativeMethod()
        return
    }
    method()
}

Réponse originale:

Vous pouvez utiliser l'approche "if let" pour tester un protocole optionnel comme celui-ci:

weak var delegate: SomeDelegateWithOptionals?

func someMethod() {
  if let delegate = delegate {
    if let theMethod = delegate.theOptionalProtocolMethod? {
      theMethod()
      return
    }
  }
  // Reaching here means the delegate doesn't exist or doesn't respond to the optional method
  alternativeMethod()
}
35
hyouuu

Il semble que vous deviez définir votre protocole en tant que sous-protocole de NSObjectProtocol ... puis vous obtiendrez la méthode respondsToSelector.

@objc protocol YourDelegate : NSObjectProtocol
{
    func yourDelegateMethod(passObject: SomeObject)
}

notez que spécifier @objc n'était pas suffisant. Vous devez également veiller à ce que le délégué réel soit une sous-classe de NSObject - ce qui, dans Swift, pourrait ne pas l'être.

11
Matej Ukmar

Si la méthode que vous testez est définie comme une méthode optionnelle dans un @ objc protocole (qui ressemble à votre cas), puis utilisez le modèle de chaînage facultatif comme suit:

if let result = object.method?(args) {
  /* method exists, result assigned, use result */
}
else { ... }

Lorsque la méthode est déclarée comme retournant Void, utilisez simplement:

if object.method?(args) { ... }

Voir:

“Méthodes d'appel via un chaînage facultatif”
Extrait de: Apple Inc. "Le langage de programmation Swift."
iBooks. https://itun.es/us/jEUH0.l

10
GoZoner

Les fonctions sont des types de première classe dans Swift. Vous pouvez donc vérifier si une fonction optionnelle définie dans un protocole a été implémentée en la comparant à nil:

if (someObject.someMethod != nil) {
    someObject.someMethod!(someArgument)
} else {
    // do something else
}
9

Dans Swift 2, Apple a introduit une nouvelle fonctionnalité appelée API availability checking, qui pourrait remplacer la méthode respondsToSelector:. La comparaison d'extrait de code suivante est copiée à partir de la WWDC2015 Session 106 Quoi de neuf dans Swift que je pensais pouvoir vous aider, veuillez le vérifier si vous avez besoin d'en savoir plus.

L'ancienne approche:

@IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
    if dropButton.respondsToSelector("setSpringLoaded:") {
        dropButton.springLoaded = true
    }
}

La meilleure approche:

@IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
    if #available(OSX 10.10.3, *) {
        dropButton.springLoaded = true
    }
}
7
tounaobun

Pour Swift 3.0

import UIKit

@objc protocol ADelegate : NSObjectProtocol {

    @objc optional func hi1()
    @objc optional func hi2(message1:String, message2:String)
}

class SomeObject : NSObject {

    weak var delegate:ADelegate?

    func run() {

        // single method
        if let methodHi1 = delegate?.hi1 {
            methodHi1()
        } else {
            print("fail h1")
        }

        // multiple parameters
        if let methodHi2 = delegate?.hi2 {
            methodHi2("superman", "batman")
        } else {
            print("fail h2")
        }
    }
}

class ViewController: UIViewController, ADelegate {

    let someObject = SomeObject()

    override func viewDidLoad() {
        super.viewDidLoad()

        someObject.delegate = self
        someObject.run()
    }

    // MARK: ADelegate
    func hi1() {

        print("Hi")
    }

    func hi2(message1: String, message2: String) {

        print("Hi \(message1) \(message2)")
    }
}
6
Kakashi

Pour Swift3

Si vous souhaitez simplement appeler la méthode, exécutez le code ci-dessous.

self.delegate?.method?()

6
Jason Yu

Actuellement (Swift 2.1), vous pouvez le vérifier de 3 manières différentes:

  1. Utilisation de respondsToSelector répondu par @Erik_at_Digit
  2. Utilisation de '?' répondu par @Sulthan

  3. Et en utilisant l'opérateur as?:

    if let delegateMe = self.delegate as? YourCustomViewController
    {
       delegateMe.onSuccess()
    }
    

Fondamentalement, cela dépend de ce que vous essayez d’atteindre:

  • Si, par exemple, la logique de votre application doit exécuter une action et que le délégué n'est pas défini ou que le délégué désigné n'a pas mis en œuvre la méthode onSuccess () (méthode du protocole), les options 1 et 3 constituent le meilleur choix, bien que je l'utilise l'option 3 qui est Swift way.
  • Si vous ne voulez rien faire lorsque le délégué est nul ou que la méthode n'est pas implémentée, utilisez l'option 2.
4
OhadM

une autre syntaxe possible par Swift ..

 if let delegate = self.delegate, method = delegate.somemethod{
        method()
    }
2
Janub

Je viens d'implémenter cela moi-même dans un projet, voir le code ci-dessous. Comme mentionné par @Christopher Pickslay, il est important de rappeler que les fonctions sont des citoyens de première classe et peuvent donc être traitées comme des variables facultatives.

@objc protocol ContactDetailsDelegate: class {

    optional func deleteContact(contact: Contact) -> NSError?
}

...

weak var delegate:ContactDetailsDelegate!

if let deleteContact = delegate.deleteContact {
    deleteContact(contact)
}
2
Bulwinkel

Comme je commençais à mettre à jour mon ancien projet vers Swift 3.2, je devais simplement changer la méthode de

respondsToSelector(selector)

à:

responds(to: selector)
1
chAlexey

J'utilise guard let else, donc cela peut faire quelques trucs par défaut si le délégué func n'est pas implémenté.

@objc protocol ViewController2Delegate: NSObjectProtocol {

    optional func viewController2(controller: ViewController2, didSomethingWithStringAndReturnVoid string: String)

    optional func viewController2(controller: ViewController2, didSomethingWithStringAndReturnString string: String) -> String
}

class ViewController2: UIViewController {

    weak var delegate: ViewController2Delegate?        

    @IBAction func onVoidButtonClicked(sender: AnyObject){

        if (delegate != nil && delegate!.respondsToSelector(Selector("viewController2:didSomethingWithStringAndReturnVoid:"))) {
            NSLog("ReturnVoid is implemented")

            delegate!.viewController2!(self, didSomethingWithStringAndReturnVoid: "dummy")
        }
        else{
            NSLog("ReturnVoid is not implemented")
            // Do something by default
        }
    }

    @IBAction func onStringButtonClicked(sender: AnyObject){

        guard let result = delegate?.viewController2?(self, didSomethingWithStringAndReturnString: "dummy") else {
            NSLog("ReturnString is not implemented")
            // Do something by default
            return
        }

        NSLog("ReturnString is implemented with result: \(result)")
    }
}
0
dichen