Je crée plusieurs classes NSView
, qui prennent toutes en charge une opération spéciale, que nous appellerons transmogrify
. À première vue, cela semble être l'endroit idéal pour un protocole:
protocol TransmogrifiableView {
func transmogrify()
}
Cependant, ce protocole n'impose pas que chaque TransmogrifiableView
soit également un NSView
. Cela signifie que toutes les méthodes NSView
que j'appelle sur TransmogrifiableView
ne taperont pas check:
let myView: TransmogrifiableView = getTransmogrifiableView()
let theSuperView = myView.superView // error: TransmogrifiableView does not have a property called 'superview'
Je ne sais pas comment exiger que toutes les classes implémentant mon protocole soient également des sous-classes de NSView
. J'ai essayé ceci:
protocol TransmogrifiableView: NSView {
func transmogrify()
}
mais Swift se plaint que les protocoles ne peuvent pas hériter des classes. Cela n'aide pas à transformer le protocole en un protocole de classe uniquement en utilisant
protocol TransmogrifiableView: class, NSView {
func transmogrify()
}
Je ne peux pas faire de TransmogrifiableView
une superclasse plutôt qu'un protocole, car certaines de mes classes TransmogrifiableView
doivent être des sous-classes d'autres vues non transmogrifiables.
Comment dois-je exiger que tous les TransmogrifiableView
soient également des NSView
? Je ne veux vraiment pas poivrer mon code avec des conversions "as
", qui sont de mauvaise forme et distrayantes.
Je pense que vous recherchez une sous-classe de NSView
. Essaye ça:
protocol TransmogrifiableView {
func transmogrify()
}
class MyNSView: NSView, TransmogrifiableView {
// do stuff.
}
Et plus tard dans le code, acceptez les objets de type MyNSView
.
Vous voulez peut-être un Extension
, voir this
extension NSView: TransmogrifiableView {
// implementation of protocol requirements goes here
}
Encore une autre option consiste à créer une classe qui contient un pointeur vers une NSView et implémente des méthodes supplémentaires. Cela vous obligera également à proxy toutes les méthodes de NSView que vous souhaitez utiliser.
class NSViewWrapper: TransmogrifiableView {
var view : NSView!
// init with the view required.
// implementation of protocol requirements goes here.
.....
// proxy all methods from NSView.
func getSuperView(){
return self.view.superView
}
}
C'est assez long et pas sympa, mais ça marchera. Je vous recommande de ne l'utiliser que si vous ne pouvez vraiment pas travailler avec des extensions (car vous avez besoin de NSViews sans la méthode supplémentaire).
À partir de Swift 4, vous pouvez maintenant définir ceci comme suit:
let myView: NSView & TransmogrifiableView
Pour plus d'informations, consultez le problème de paiement # 156 Subclass Existentials
Il existe une solution de contournement en utilisant des types associés pour appliquer la sous-classe:
protocol TransmogrifiableView {
associatedtype View: NSView = Self
func transmogrify()
}
class MyView: NSView, TransmogrifiableView { ... } // compiles
class MyOtherClass: TransmogrifiableView { ... } // doesn't compile
Vous pouvez utiliser quelque chose comme ceci:
protocol TransmogrifiableView where Self:NSView {}
Cela nécessite que toutes les instances créées dont l'une est conforme au protocole TransmogrifiableView soient sous-classées avec NSView
Pour Swift 4, basé sur la perspicacité de @ Antoine:
Créez un protocole, puis utilisez une typealias pour donner un nom plus propre à un type conforme à la fois à une classe et au protocole.
protocol Transmogrifiable {
func transmogrify()
}
typealias TransmogrifiableView = NSView & Transmogrifiable
Vous pouvez ensuite définir une classe qui hérite de ce type ....
class ATransmogView: TransmogrifiableView {
func transmogrify() {
print("I'm transmogging")
}
}
.... ou définissez une classe qui hérite du protocole et d'une sous-classe
// this also qualifies as a TransmogrifiableView
class BTransmogView: NSTextView, Transmogrifiable {
func transmogrify() {
print("I'm transmogging too")
}
}
Vous pouvez maintenant le faire.
func getTransmogrifiableView() -> TransmogrifiableView {
return someBool ? ATransmogView() : BTransmogView()
}
Et cela se compile maintenant.
let myView: TransmogrifiableView = getTransmogrifiableView()
let theSuperView = myView.superView
Selon la définition, un protocole déclare simplement les exigences des "méthodes, propriétés an autres exigences". Et par "autres exigences", cela signifie qu'une superclasse n'en fait pas partie.
Un protocole définit un schéma de méthodes, de propriétés et d'autres exigences qui conviennent à une tâche ou à un élément de fonctionnalité particulier.
Pour l'instant, je ne vois pas de solution propre. Il est possible d'utiliser une clause where
- pour définir un type qui est un NSView
et conforme au TransmogrifiableView
comme ceci:
class MyClass<T where T: NSView, T: TransmogrifiableView> {
var aTransmogrifiableNSView: T
}
Ou vous pouvez utiliser une autre superclasse:
protocol TransmogrifiableViewProtocol {
func transmogrify()
}
class TransmogrifiableView: NSView, TransmogrifiableViewProtocol {
func transmogrify() {
assert(false, "transmogrify() must be overwritten!")
}
}
class AnImplementedTransmogrifiableView: TransmogrifiableView {
func transmogrify() {
println("Do the transmogrification...")
}
}
Au final, les deux solutions ne sont pas propres et ne me satisferaient pas. Peut-être qu'un mot clé abstract
- sera ajouté à Swift un jour?
vérifiez cette solution ici ... Swift - méthode de classe qui doit être remplacée par la sous-classe
Cette solution permet d'hériter d'une classe et également d'avoir la vérification du temps de compilation du protocole. :)
Ce n'est toujours pas la solution idéale, mais voici un modèle que j'utilise à l'occasion:
Cela permet à votre classe de base d'appeler les méthodes de protocole tout en forçant les enfants à les implémenter (par exemple, self.myDelegate? .MyProtocolMethod).