2017-07-13 2 views
3

Langue: Swift 3Comment appeler une fonction en option à l'aide de sélection et NotificationCenter

IDE: XCode 8.3.2 (8E2002)

J'ai un protocole avec une fonction optionnelle foo

@objc protocol SomeProtocol { 
    @objc optional func foo(_ notification: Notification) 
} 

extension SomeProtocol { 
    func listenToFoo() { 
     NotificationCenter.default.addObserver(self, selector: #selector(self.foo(_:)), name: NSNotification.Name(rawValue: "UltimateNotificationKeyLOL"), object: nil) 
    } 
} 

Si j'étire ce code à classe, par exemple UIViewController.

class CrashingViewController: UIViewController { 
    override func viewDidLoad() { 
     super.viewDidLoad() 

     self.listenToFoo() 
    } 
} 

extension CrashingViewController: SomeProtocol { } 

Maintenant vient ici le problème, étant donné que foo est une fonction optionnelle, si quelqu'un envoie un Notification la NSNotification.Name(rawValue: "UltimateNotificationKeyLOL") touche l'application se bloque parce que je ne l'ai pas encore mis en œuvre foo. Donc dans ce cas, le code ci-dessus provoquera un crash.

Cependant, si je fais ce

class GodzillaViewController: UIViewController { 
    override func viewDidLoad() { 
     super.viewDidLoad() 

     self.listenToFoo() 
    } 
} 

extension GodzillaViewController: SomeProtocol { 
    func foo(_ notification: Notification) { 
     print("lol") 
    } 
} 

Aucun accident est créé depuis foo(_:) est pas une option plus.

également: Ce code ne peut #selector(self.foo?(_:))

Question: Est-il possible d'avoir un sélecteur appeler une fonction facultative sans écraser l'application?

Répondre

4

Si je où vous je ferais un protocole complet rapide comme ceci:

// Protocol declaration 
protocol SomeProtocol { 
    func foo(_ notification: Notification) 
} 

// Provide default implementation for optional methods of SomeProtocol 
extension SomeProtocol { 
    func foo(_ notification: Notification) {} 
} 

// Extend SomeProtocol with additional methods 
extension SomeProtocol { 
    func listenToFoo() { 
     NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "UltimateNotificationKeyLOL"), object: nil, queue: nil) { (notification) in 
      self.foo(notification) 
     } 
    } 
} 

Comme vous pouvez le voir, le faire comme cela a plusieurs avantages:

  1. Vous avez une Swift seulement Code (pas @objc)
  2. en ajoutant une implémentation par défaut de foo, vous avez la fonction optionnelle.
  3. Votre notification peut toujours appeler foo sans se bloquer car elle ira à l'implémentation par défaut si nécessaire
  4. Vous pouvez même ajouter du code à la méthode par défaut si vous voulez toujours faire quelque chose!

MISE À JOUR

Vous pouvez voir dans la fonction listenToFoo() que j'ai utilisé une fonction différente addObserver qui utilise une fermeture au lieu, la raison étant que #selector exige toujours la fonction d'être exposés à @objc et la fermeture ne fonctionne pas:

func addObserver (nomNomNom: NSNotification.Nom ?, objet obj: Any ?, queue: OperationQueue?, En utilisant le bloc: @escaping (Notification) -> Void) -> NSObjectProtocol

+0

@Hamish est correct, vous ne pouvez pas utiliser NotificationCenter pour les fonctions qui ne sont pas exposées à Obj-C –

+0

@Hamish Vous avez raison, je mis à jour la réponse pour utiliser une autre fonction 'addObserver' qui ne nécessite pas @objc! Merci – Thomas

+1

@ZonilyJame La méthode '# selector' est ce qui nécessite l'exposition d'une méthode à ObjC. Il y a bien sûr d'autres possibilités comme celle que je change, ou si vous voulez vraiment utiliser des sélecteurs pour une raison quelconque, vous devriez regarder la variante Swift qui fonctionne quelque chose comme ça à la place 'Selector (" foo (_ :) ")' – Thomas