Je me suis finalement motivé à mettre à jour ma réponse, vous pouvez reconsidérer son acceptation.
akashivskyy
@akashivskyy J'accepte votre réponse car elle présente mieux toutes les options disponibles et leurs avantages et inconvénients.
Selvin
Réponses:
506
1. Utilisation d'implémentations par défaut (de préférence).
protocolMyProtocol{func doSomething()}extensionMyProtocol{func doSomething(){/* return a default value or just leave empty */}}structMyStruct:MyProtocol{/* no compile error */}
Les avantages
Aucun runtime Objective-C n'est impliqué (enfin, pas explicitement au moins). Cela signifie que vous pouvez lui conformer des structures, des énumérations et des non- NSObjectclasses. En outre, cela signifie que vous pouvez profiter d'un puissant système générique.
Vous pouvez toujours être sûr que toutes les exigences sont remplies lorsque vous rencontrez des types conformes à ce protocole. C'est toujours une implémentation concrète ou une implémentation par défaut. C'est ainsi que les "interfaces" ou les "contrats" se comportent dans d'autres langues.
Désavantages
Pour les non- Voidexigences, vous devez avoir une valeur par défaut raisonnable , ce qui n'est pas toujours possible. Cependant, lorsque vous rencontrez ce problème, cela signifie que cette exigence ne devrait vraiment pas avoir d'implémentation par défaut, ou que vous vous êtes trompé lors de la conception de l'API.
Vous ne pouvez pas faire la distinction entre une implémentation par défaut et aucune implémentation , du moins sans résoudre ce problème avec des valeurs de retour spéciales. Prenons l'exemple suivant:
Si vous fournissez une implémentation par défaut qui revient juste true- c'est bien au premier coup d'œil. Maintenant, considérez le pseudo-code suivant:
finalclassSomeParser{func parse(data:Data)->[Any]{if/* delegate.validate(value:) is not implemented */{/* parse very fast without validating */}else{/* parse and validate every value */}}}
Il n'y a aucun moyen d'implémenter une telle optimisation - vous ne pouvez pas savoir si votre délégué implémente une méthode ou non.
Bien qu'il existe un certain nombre de façons différentes de résoudre ce problème (en utilisant des fermetures facultatives, différents objets délégués pour différentes opérations pour n'en nommer que quelques-uns), cet exemple présente clairement le problème.
2. Utilisation @objc optional.
@objc protocolMyProtocol{@objc optionalfunc doSomething()}classMyClass:NSObject,MyProtocol{/* no compile error */}
Les avantages
Aucune implémentation par défaut n'est nécessaire. Vous venez de déclarer une méthode facultative ou une variable et vous êtes prêt à partir.
Désavantages
Il limite considérablement les capacités de votre protocole en exigeant que tous les types conformes soient compatibles avec Objective-C. Cela signifie que seules les classes qui héritent de NSObjectpeuvent se conformer à ce protocole. Pas de structures, pas d'énumérations, pas de types associés.
Vous devez toujours vérifier si une méthode facultative est implémentée en appelant ou en vérifiant éventuellement si le type conforme l'implémente. Cela peut introduire beaucoup de passe-partout si vous appelez souvent des méthodes facultatives.
Regardez une méthode améliorée de méthodes optionnelles dans swift en utilisant une extension (ci-dessous)
Daniel Kanaan
1
Et comment tester la prise en charge d'une méthode de protocole facultative dans une instance? respondsToSelector?
devios1
3
Ne fais pas ça! Swift ne prend pas en charge la méthode facultative dans les protocoles pour une raison.
fpg1503
2
Cette méthode ne prend pas en charge les options dans les paramètres. C'est-à-dire que vous ne pouvez pas le faireoptional func doSomething(param: Int?)
SoftDesigner
4
Certes, cette réponse est essentiellement fausse de nos jours, des années plus tard. Aujourd'hui, dans Swift, vous ajoutez simplement une extension pour l'implémentation par défaut, c'est un aspect de base de Swift. (Comme le montrent toutes les réponses modernes ci-dessous.) Il serait tout simplement faux d'ajouter le drapeau objc, de nos jours.
Fattie
394
Dans Swift 2 et versions ultérieures, il est possible d'ajouter des implémentations par défaut d'un protocole. Cela crée une nouvelle façon de méthodes facultatives dans les protocoles.
protocolMyProtocol{func doSomethingNonOptionalMethod()func doSomethingOptionalMethod()}extensionMyProtocol{func doSomethingOptionalMethod(){// leaving this empty }}
Ce n'est pas une très bonne façon de créer des méthodes de protocole facultatives, mais vous donne la possibilité d'utiliser des structures dans des rappels de protocole.
C'est probablement la façon la plus propre de le faire dans Swift. Dommage que cela ne fonctionne pas avant Swift 2.0.
Entalpi
13
@MattQuiros Je trouve que vous avez en fait besoin de déclarer la fonction dans la définition du protocole, sinon la fonction d'extension no-op n'est pas remplacée dans vos classes conformes au protocole.
Ian Pearce
3
@IanPearce est correct, et cela semble être par conception. Dans le discours sur la "programmation orientée protocole" (408) à la WWDC, ils parlent des méthodes du protocole principal qui sont des "points de personnalisation" offerts aux types conformes. Un point de personnalisation requis ne reçoit pas de définition dans une extension; un point facultatif le fait. Les méthodes sur le protocole qui ne devraient généralement pas être personnalisées sont entièrement déclarées / définies dans l'extension pour interdire aux types conformes de personnaliser, sauf si vous effectuez une conversion spécifique vers son dynamicType pour montrer que vous souhaitez l'implémentation personnalisée du conformateur.
matthias
4
@FranklinYu Vous pouvez le faire, mais vous modifiez ensuite la conception de votre API avec des «lancers» là où ils ne sont en fait pas nécessaires. J'aime plus l'idée de "micro protocoles". Par exemple, chaque méthode confirme un protocole et vous pouvez ensuite vérifier: si l'objet est un protocole
Darko
3
@Antoine Je pense que vous devriez rendre la fonction dans l'extension publique, car les fonctions dans les protocoles sont publiques par définition. Votre solution ne fonctionnera pas lorsque le protocole sera utilisé en dehors de son module.
Vadim Eisenberg du
39
Puisqu'il y a quelques réponses sur la façon d'utiliser le modificateur facultatif et l' attribut @objc pour définir le protocole d'exigence facultatif, je vais donner un exemple sur la façon d'utiliser les extensions de protocole pour définir le protocole facultatif.
Le code ci-dessous est Swift 3. *.
/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`protocolCancelable{/// default implementation is empty.func cancel()}extensionCancelable{func cancel(){}}classPlane:Cancelable{//Since cancel() have default implementation, that is optional to class Plane}let plane =Plane()
plane.cancel()//Print out *UnitedAirlines can't cancelable*
Veuillez noter que les méthodes d'extension de protocole ne peuvent pas être invoquées par le code Objective-C, et pire encore, l'équipe Swift ne le réparera pas. https://bugs.swift.org/browse/SR-492
Bon travail! Cela devrait être la bonne réponse car elle résout le problème sans impliquer le runtime Objective-C.
Lukas
vraiment sympa!
tania_S
from swift documentation - "Les exigences de protocole avec les implémentations par défaut fournies par les extensions sont distinctes des exigences de protocole facultatives. Bien que les types conformes n'aient pas à fournir leur propre implémentation non plus, les exigences avec les implémentations par défaut peuvent être appelées sans chaînage facultatif."
Mec Os
34
Les autres réponses ici impliquant le marquage du protocole comme "@objc" ne fonctionnent pas lors de l'utilisation de types spécifiques à Swift.
structInfo{var height:Intvar weight:Int}@objc protocolHealth{func isInfoHealthy(info:Info)->Bool}//Error"Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
Afin de déclarer des protocoles facultatifs qui fonctionnent bien avec swift, déclarez les fonctions en tant que variables au lieu de func.
classHuman:Health{var isInfoHealthy:(Info)->(Bool)?={ info inif info.weight <200&& info.height >72{returntrue}returnfalse}//Or leave out the implementation and declare it as: //var isInfoHealthy: (Info) -> (Bool)?}
Vous pouvez ensuite utiliser "?" vérifier si la fonction a été implémentée ou non
Avec cette solution, vous ne pouvez pas accéder à vous-même depuis l'une des fonctions de protocole. Cela peut entraîner des problèmes dans certains cas!
Seuls les classes, protocoles, méthodes et propriétés peuvent utiliser @objc. Si vous utilisez un paramètre Enum dans la définition de la méthode du protocole @ objc, vous êtes condamné.
khunshan
1
@khunshan, cette méthode ne nécessite rien d'être marqué @ objc, à quoi faites-vous référence?
Zag
il s'agit d'une information sur le sujet qui ne peut pas être utilisée comme énumération entre swift et objc et les autres instructions peuvent être pontées avec le mot clé @objc.
khunshan
34
Voici un exemple concret avec le modèle de délégation.
Je pense qu'avant de demander comment vous pouvez implémenter une méthode de protocole facultative, vous devriez vous demander pourquoi vous devriez en implémenter une.
Si nous considérons les protocoles rapides comme une interface dans la programmation orientée objet classique, les méthodes facultatives n'ont pas beaucoup de sens, et peut-être une meilleure solution serait de créer une implémentation par défaut, ou de séparer le protocole en un ensemble de protocoles (peut-être avec certaines relations d'héritage entre eux) pour représenter la combinaison possible de méthodes dans le protocole.
Voici un exemple très simple pour les classes rapides UNIQUEMENT, et non pour les structures ou les énumérations. Notez que la méthode de protocole étant facultative, a deux niveaux de chaînage facultatif en jeu. La classe adoptant le protocole a également besoin de l'attribut @objc dans sa déclaration.
@objc protocolCollectionOfDataDelegate{optionalfunc indexDidChange(index:Int)}@objc classRootView:CollectionOfDataDelegate{var data =CollectionOfData()init(){
data.delegate =self
data.indexIsNow()}func indexDidChange(index:Int){
println("The index is currently: \(index)")}}classCollectionOfData{var index :Int?weakvar delegate :CollectionOfDataDelegate?func indexIsNow(){
index =23
delegate?.indexDidChange?(index!)}}
Pouvez-vous décrire un peu plus la partie " deux niveaux d'option en jeu ", à savoir delegate?.indexDidChange?(index!):?
Unheilig
2
Si nous avions écrit le protocole pour avoir une méthode non optionnelle comme celle-ci: protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) } alors vous l'appelleriez sans le point d'interrogation: delegate?.indexDidChange(index!) lorsque vous définissez une exigence facultative pour une méthode dans un protocole, le type qui s'y conformera pourrait ne PAS implémenter cette méthode , donc le ?est utilisé pour vérifier l'implémentation et s'il n'y en a pas, le programme ne plantera pas. @Unheilig
Blessing Lopes
weak var delegate : CollectionOfDataDelegate?(assurer une référence faible?)
Alfie Hanssen
@BlessingLopes Pouvez-vous ajouter votre explication de l' delegate?utilisation à votre réponse? Cette information devrait vraiment appartenir à d'autres à l'avenir. Je veux voter positivement, mais cette information devrait vraiment être dans la réponse.
Johnathon Sullinger
3
si vous voulez le faire en pure swift, le mieux est de fournir une implémentation par défaut si vous retournez un type Swift comme par exemple struct avec des types Swift
Il existe deux façons de créer une méthode facultative dans le protocole rapide.
1 - La première option consiste à marquer votre protocole à l'aide de l'attribut @objc. Bien que cela signifie qu'il ne peut être adopté que par des classes, cela signifie que vous marquez les méthodes individuelles comme étant facultatives comme ceci:
2 - Une manière plus rapide: cette option est meilleure. Écrivez des implémentations par défaut des méthodes facultatives qui ne font rien, comme ceci.
protocolMyProtocol{func optionalMethod()func notOptionalMethod()}extensionMyProtocol{func optionalMethod(){//this is a empty implementation to allow this method to be optional
}}
Swift a une fonctionnalité appelée extension qui nous permet de fournir une implémentation par défaut pour les méthodes que nous voulons être facultatives.
Définissez la fonction dans le protocole et créez l'extension pour ce protocole, puis créez une implémentation vide pour la fonction que vous souhaitez utiliser comme facultatif.
Pour définir OptionalProtocoldans swift, vous devez utiliser le @objcmot-clé before Protocoldeclaration et attribute/ methoddeclaration dans ce protocole. Voici un exemple de propriété facultative d'un protocole.
@objc protocolProtocol{@objc optionalvar name:String?}classMyClass:Protocol{// No error
}
Bien que cela puisse répondre à la question, il est préférable d'ajouter une description de la façon dont cette réponse peut aider à résoudre le problème. Veuillez lire Comment écrire une bonne réponse pour en savoir plus.
Roshana Pitigala
-23
Mettez le @optionaldevant des méthodes ou des propriétés.
Réponses:
1. Utilisation d'implémentations par défaut (de préférence).
Les avantages
Aucun runtime Objective-C n'est impliqué (enfin, pas explicitement au moins). Cela signifie que vous pouvez lui conformer des structures, des énumérations et des non-
NSObject
classes. En outre, cela signifie que vous pouvez profiter d'un puissant système générique.Vous pouvez toujours être sûr que toutes les exigences sont remplies lorsque vous rencontrez des types conformes à ce protocole. C'est toujours une implémentation concrète ou une implémentation par défaut. C'est ainsi que les "interfaces" ou les "contrats" se comportent dans d'autres langues.
Désavantages
Pour les non-
Void
exigences, vous devez avoir une valeur par défaut raisonnable , ce qui n'est pas toujours possible. Cependant, lorsque vous rencontrez ce problème, cela signifie que cette exigence ne devrait vraiment pas avoir d'implémentation par défaut, ou que vous vous êtes trompé lors de la conception de l'API.Vous ne pouvez pas faire la distinction entre une implémentation par défaut et aucune implémentation , du moins sans résoudre ce problème avec des valeurs de retour spéciales. Prenons l'exemple suivant:
Si vous fournissez une implémentation par défaut qui revient juste
true
- c'est bien au premier coup d'œil. Maintenant, considérez le pseudo-code suivant:Il n'y a aucun moyen d'implémenter une telle optimisation - vous ne pouvez pas savoir si votre délégué implémente une méthode ou non.
Bien qu'il existe un certain nombre de façons différentes de résoudre ce problème (en utilisant des fermetures facultatives, différents objets délégués pour différentes opérations pour n'en nommer que quelques-uns), cet exemple présente clairement le problème.
2. Utilisation
@objc optional
.Les avantages
Désavantages
Il limite considérablement les capacités de votre protocole en exigeant que tous les types conformes soient compatibles avec Objective-C. Cela signifie que seules les classes qui héritent de
NSObject
peuvent se conformer à ce protocole. Pas de structures, pas d'énumérations, pas de types associés.Vous devez toujours vérifier si une méthode facultative est implémentée en appelant ou en vérifiant éventuellement si le type conforme l'implémente. Cela peut introduire beaucoup de passe-partout si vous appelez souvent des méthodes facultatives.
la source
respondsToSelector
?optional func doSomething(param: Int?)
Dans Swift 2 et versions ultérieures, il est possible d'ajouter des implémentations par défaut d'un protocole. Cela crée une nouvelle façon de méthodes facultatives dans les protocoles.
Ce n'est pas une très bonne façon de créer des méthodes de protocole facultatives, mais vous donne la possibilité d'utiliser des structures dans des rappels de protocole.
J'ai écrit un petit résumé ici: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
la source
Puisqu'il y a quelques réponses sur la façon d'utiliser le modificateur facultatif et l' attribut @objc pour définir le protocole d'exigence facultatif, je vais donner un exemple sur la façon d'utiliser les extensions de protocole pour définir le protocole facultatif.
Le code ci-dessous est Swift 3. *.
Veuillez noter que les méthodes d'extension de protocole ne peuvent pas être invoquées par le code Objective-C, et pire encore, l'équipe Swift ne le réparera pas. https://bugs.swift.org/browse/SR-492
la source
Les autres réponses ici impliquant le marquage du protocole comme "@objc" ne fonctionnent pas lors de l'utilisation de types spécifiques à Swift.
Afin de déclarer des protocoles facultatifs qui fonctionnent bien avec swift, déclarez les fonctions en tant que variables au lieu de func.
Et puis implémentez le protocole comme suit
Vous pouvez ensuite utiliser "?" vérifier si la fonction a été implémentée ou non
la source
Voici un exemple concret avec le modèle de délégation.
Configurez votre protocole:
Définissez le délégué sur une classe et implémentez le protocole. Vérifiez que la méthode facultative n'a pas besoin d'être implémentée.
Une chose importante est que la méthode facultative est facultative et nécessite un "?" lors de l'appel. Mentionnez le deuxième point d'interrogation.
la source
Dans Swift 3.0
Cela vous fera gagner du temps.
la source
@objc
est-elle nécessaire pour tous les membres maintenant?required
flag, mais avec des erreurs:required
ne peut être utilisé que sur les déclarations 'init'.optional
mot clé avant chaque méthode.la source
@objc
, pas seulement le protocole.Une approche Swift pure avec héritage de protocole:
la source
Pour illustrer la mécanique de la réponse d'Antoine:
la source
Je pense qu'avant de demander comment vous pouvez implémenter une méthode de protocole facultative, vous devriez vous demander pourquoi vous devriez en implémenter une.
Si nous considérons les protocoles rapides comme une interface dans la programmation orientée objet classique, les méthodes facultatives n'ont pas beaucoup de sens, et peut-être une meilleure solution serait de créer une implémentation par défaut, ou de séparer le protocole en un ensemble de protocoles (peut-être avec certaines relations d'héritage entre eux) pour représenter la combinaison possible de méthodes dans le protocole.
Pour plus de lecture, voir https://useyourloaf.com/blog/swift-optional-protocol-methods/ , qui donne un excellent aperçu de cette question.
la source
Légèrement éloigné du sujet de la question d'origine, mais il se fonde sur l'idée d'Antoine et j'ai pensé que cela pourrait aider quelqu'un.
Vous pouvez également rendre les propriétés calculées facultatives pour les structures avec des extensions de protocole.
Vous pouvez rendre une propriété sur le protocole facultative
Implémenter la propriété calculée factice dans l'extension de protocole
Et maintenant, vous pouvez utiliser des structures qui implémentent ou non la propriété facultative
J'ai également écrit comment faire des propriétés facultatives dans les protocoles Swift sur mon blog , que je garderai à jour au cas où les choses changeraient avec les versions de Swift 2.
la source
Comment créer des méthodes de délégué facultatives et obligatoires.
la source
Voici un exemple très simple pour les classes rapides UNIQUEMENT, et non pour les structures ou les énumérations. Notez que la méthode de protocole étant facultative, a deux niveaux de chaînage facultatif en jeu. La classe adoptant le protocole a également besoin de l'attribut @objc dans sa déclaration.
la source
delegate?.indexDidChange?(index!)
:?protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) }
alors vous l'appelleriez sans le point d'interrogation:delegate?.indexDidChange(index!)
lorsque vous définissez une exigence facultative pour une méthode dans un protocole, le type qui s'y conformera pourrait ne PAS implémenter cette méthode , donc le?
est utilisé pour vérifier l'implémentation et s'il n'y en a pas, le programme ne plantera pas. @Unheiligweak var delegate : CollectionOfDataDelegate?
(assurer une référence faible?)delegate?
utilisation à votre réponse? Cette information devrait vraiment appartenir à d'autres à l'avenir. Je veux voter positivement, mais cette information devrait vraiment être dans la réponse.si vous voulez le faire en pure swift, le mieux est de fournir une implémentation par défaut si vous retournez un type Swift comme par exemple struct avec des types Swift
exemple :
alors vous pouvez implémenter un protocole sans définir chaque fonction
la source
Il existe deux façons de créer une méthode facultative dans le protocole rapide.
1 - La première option consiste à marquer votre protocole à l'aide de l'attribut @objc. Bien que cela signifie qu'il ne peut être adopté que par des classes, cela signifie que vous marquez les méthodes individuelles comme étant facultatives comme ceci:
2 - Une manière plus rapide: cette option est meilleure. Écrivez des implémentations par défaut des méthodes facultatives qui ne font rien, comme ceci.
Swift a une fonctionnalité appelée extension qui nous permet de fournir une implémentation par défaut pour les méthodes que nous voulons être facultatives.
la source
Une option consiste à les stocker en tant que variables de fonction facultatives:
la source
Définissez la fonction dans le protocole et créez l'extension pour ce protocole, puis créez une implémentation vide pour la fonction que vous souhaitez utiliser comme facultatif.
la source
Pour définir
Optional
Protocol
dans swift, vous devez utiliser le@objc
mot-clé beforeProtocol
declaration etattribute
/method
declaration dans ce protocole. Voici un exemple de propriété facultative d'un protocole.la source
Mettez le
@optional
devant des méthodes ou des propriétés.la source
@optional
n'est même pas le bon mot-clé. C'est le casoptional
, et vous devez déclarer la classe et le protocole avec l'@objc
attribut.