Est-ce possible à Swift? Si non, existe-t-il une solution de contournement?
Puisqu'il y a quelques réponses sur la façon d'utiliser optional modificateur et l'attribut @objc pour définir le protocole d'exigence facultatif, je vais donner un exemple de l'utilisation des 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()`
protocol Cancelable {
/// default implementation is empty.
func cancel()
}
extension Cancelable {
func cancel() {}
}
class Plane: Cancelable {
//Since cancel() have default implementation, that is optional to class Plane
}
let plane = Plane()
plane.cancel()
// Print out *United Airlines 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 le pire est que l'équipe de Swift ne le réglera pas. https://bugs.Swift.org/browse/SR-492
Si vous souhaitez utiliser des méthodes facultatives, vous devez marquez votre protocole avec l'attribut @objc
:
@objc protocol MyProtocol {
@objc optional func doSomething()
}
class MyClass : MyProtocol {
// no error
}
Sinon, les protocoles Swift se comportent comme des interfaces sur d'autres langages OO, où toutes les méthodes définies dans les interfaces doivent être implémentées.
À partir de Swift 2, il est possible d’ajouter des implémentations par défaut d’un protocole. Cela crée une nouvelle méthode de méthodes facultatives dans les protocoles.
protocol MyProtocol {
func doSomethingNonOptionalMethod()
func doSomethingOptionalMethod()
}
extension MyProtocol {
func doSomethingOptionalMethod(){
// leaving this empty
}
}
Ce n'est pas vraiment un moyen agréable de créer des méthodes de protocole facultatives, mais vous donne la possibilité d'utiliser des structures dans les rappels de protocole.
J'ai écrit un petit résumé ici: https://www.avanderlee.com/Swift-2-0/optional-protocol-methods/
Les autres réponses impliquent ici de marquer le protocole comme "@objc" ne fonctionnent pas lorsqu’on utilise des types spécifiques à Swift.
struct Info {
var height: Int
var weight: Int
}
@objc protocol Health {
func isInfoHealthy(info: Info) -> Bool
}
//Error "Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
Pour déclarer des protocoles optionnels qui fonctionnent bien avec Swift, déclarez les fonctions en tant que variables au lieu de func.
protocol Health {
var isInfoHealthy: (Info) -> (Bool)? { get set }
}
Et puis implémenter le protocole comme suit
class Human: Health {
var isInfoHealthy: (Info) -> (Bool)? = { info in
if info.weight < 200 && info.height > 72 {
return true
}
return false
}
//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
func returnEntity() -> Health {
return Human()
}
var anEntity: Health = returnEntity()
var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))?
//"isHealthy" is true
Dans Swift 3.0
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
Cela vous fera gagner du temps.
Voici un exemple concret avec le motif de délégation.
Configurez votre protocole:
@objc protocol MyProtocol:class
{
func requiredMethod()
optional func optionalMethod()
}
class MyClass: NSObject
{
weak var delegate:MyProtocol?
func callDelegate()
{
delegate?.requiredMethod()
delegate?.optionalMethod?()
}
}
Définissez le délégué sur une classe et implémentez le protocole. Voir que la méthode optionnelle n'a pas besoin d'être implémentée.
class AnotherClass: NSObject, MyProtocol
{
init()
{
super.init()
let myInstance = MyClass()
myInstance.delegate = self
}
func requiredMethod()
{
}
}
Une chose importante est que la méthode optionnelle est optionnelle et nécessite un "?" en appelant. Mentionnez le deuxième point d'interrogation.
delegate?.optionalMethod?()
optional
avant chaque méthode. Une approche purement Swift avec héritage de protocole:
//Required methods
protocol MyProtocol {
func foo()
}
//Optional methods
protocol MyExtendedProtocol: MyProtocol {
func bar()
}
class MyClass {
var delegate: MyProtocol
func myMethod() {
(delegate as? MyExtendedProtocol).bar()
}
}
Pour illustrer les mécanismes de la réponse d'Antoine:
protocol SomeProtocol {
func aMethod()
}
extension SomeProtocol {
func aMethod() {
print("extensionImplementation")
}
}
class protocolImplementingObject: SomeProtocol {
}
class protocolImplementingMethodOverridingObject: SomeProtocol {
func aMethod() {
print("classImplementation")
}
}
let noOverride = protocolImplementingObject()
let override = protocolImplementingMethodOverridingObject()
noOverride.aMethod() //prints "extensionImplementation"
override.aMethod() //prints "classImplementation"
Je pense qu'avant de demander comment vous pouvez implémenter une méthode de protocole optionnelle, vous devriez demander pourquoi vous devriez en implémenter un.
Si nous considérons les protocoles Swift comme une interface dans la programmation orientée objet classique, les méthodes optionnelles n’ont pas beaucoup de sens et une meilleure solution consisterait peut-être à créer une implémentation par défaut ou à séparer le protocole en un ensemble de avec quelques relations d’héritage entre eux) pour représenter la combinaison possible de méthodes dans le protocole.
Pour en savoir plus, voir https://useyourloaf.com/blog/Swift-optional-protocol-methods/ , qui donne un excellent aperçu de cette question.
Un peu en dehors du sujet de la question initiale, mais cela fonde 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
protocol SomeProtocol {
var required: String { get }
var optional: String? { get }
}
Implémenter la propriété factice calculée dans l'extension de protocole
extension SomeProtocol {
var optional: String? { return nil }
}
Et maintenant, vous pouvez utiliser des structures qui ont ou non la propriété optionnelle implémentée
struct ConformsWithoutOptional {
let required: String
}
struct ConformsWithOptional {
let required: String
let optional: String?
}
J’ai également expliqué comment utiliser les propriétés facultatives dans les protocoles Swift sur mon blog , que je tiendrai à jour au cas où quelque chose changerait dans les versions de Swift 2.
si vous voulez le faire en Swift pur, le meilleur moyen est de fournir un élément d'implémentation par défaut si vous renvoyez un type Swift comme par exemple struct avec des types Swift
exemple :
struct magicDatas {
var damagePoints : Int?
var manaPoints : Int?
}
protocol magicCastDelegate {
func castFire() -> magicDatas
func castIce() -> magicDatas
}
extension magicCastDelegate {
func castFire() -> magicDatas {
return magicDatas()
}
func castIce() -> magicDatas {
return magicDatas()
}
}
alors vous pouvez implémenter le protocole sans définir chaque func
Voici un exemple très simple pour Swift Classes SEULEMENT, et non pour les structures ou les énumérations . Notez que la méthode du protocole étant optionnelle, elle comporte deux niveaux d’enchaînement facultatif. De plus, la classe adoptant le protocole a besoin de l'attribut @objc dans sa déclaration.
@objc protocol CollectionOfDataDelegate{
optional func indexDidChange(index: Int)
}
@objc class RootView: CollectionOfDataDelegate{
var data = CollectionOfData()
init(){
data.delegate = self
data.indexIsNow()
}
func indexDidChange(index: Int) {
println("The index is currently: \(index)")
}
}
class CollectionOfData{
var index : Int?
weak var delegate : CollectionOfDataDelegate?
func indexIsNow(){
index = 23
delegate?.indexDidChange?(index!)
}
}
Comment créer des méthodes de délégation facultatives et obligatoires
@objc protocol InterViewDelegate:class {
@objc optional func optfunc() // This is optional
func requiredfunc()// This is required
}