Remplacement de la propriété superclasse avec un type différent dans Swift

Dans Swift, quelqu’un peut-il expliquer comment remplacer une propriété d’une super-classe par un autre object sous-classé de la propriété d’origine?

Prenons cet exemple simple:

class Chassis {} class RacingChassis : Chassis {} class Car { let chassis = Chassis() } class RaceCar: Car { override let chassis = RacingChassis() //Error here } 

Cela donne l’erreur:

 Cannot override with a stored property 'chassis' 

Si j’ai un châssis en tant que “var”, je reçois l’erreur:

 Cannot override mutable property 'chassis' of type 'Chassis' with covariant type 'RacingChassis' 

La seule chose que je pourrais trouver dans le guide sous “Propriétés de substitution” indique que nous devons remplacer le getter et le setter, ce qui peut fonctionner pour changer la valeur de la propriété (si elle est “var”), mais qu’en est-il de la modification de la classe de propriété ?

Swift ne vous permet pas de modifier le type de classe des variables ou des propriétés. Au lieu de cela, vous pouvez créer une variable supplémentaire dans la sous-classe qui gère le nouveau type de classe:

 class Chassis {} class RacingChassis : Chassis {} class Car { var chassis = Chassis() } class RaceCar: Car { var racingChassis = RacingChassis() override var chassis: Chassis { get { return racingChassis } set { if newValue is RacingChassis { racingChassis = newValue as RacingChassis } else { println("incorrect chassis type for racecar") } } } } 

Il semble qu’on ne puisse pas déclarer une propriété avec la syntaxe let et la remplacer par var dans sa sous-classe ou vice versa, ce qui peut être dû au fait que l’implémentation de la superclasse ne s’attend pas à ce qu’elle change une fois initialisée. Donc, dans ce cas, la propriété doit également être déclarée avec ‘var’ dans la superclasse pour correspondre à la sous-classe (comme indiqué dans l’extrait de code ci-dessus). Si on ne peut pas changer le code source dans la superclasse, alors il vaut mieux détruire le RaceCar actuel et créer un nouveau RaceCar chaque fois que le châssis doit être muté.

Cela semble fonctionner

 class Chassis { func description() -> Ssortingng { return "Chassis" } } class RacingChassis : Chassis { override func description() -> Ssortingng { return "Racing Chassis" } func racingChassisMethod() -> Ssortingng { return "Wrooom" } } class Car { let chassis = Chassis() } class RaceCar: Car { override var chassis: RacingChassis { get { return self.chassis } set { self.chassis = newValue } } } var car = Car() car.chassis.description() var raceCar = RaceCar() raceCar.chassis.description() raceCar.chassis.racingChassisMethod() 

Essaye ça:

 class Chassis{ var chassis{ return "chassis" } } class RacingChassis:Chassis{ var racing{ return "racing" } } class Car { let chassis: Type init(chassis:Type){ self.chassis = chassis } } class RaceCar: Car { var description{ return self.chassis.racing } } 

Alors:

 let racingChassis = RacingChassis() let raceCar = RaceCar(chassis:racingChassis) print(raceCar.description) //output:racing 

Détail dans http://www.mylonly.com/14957025459875.html

Le Solution Dash fourni fonctionne bien sauf que la super classe doit être déclarée avec le mot-clé let plutôt que var. Voici une solution possible mais NON RECOMMANDÉE!

La solution ci-dessous sera compilée avec Xcode 6.2, SWIFT 1.1 (si toutes les classes sont dans des fichiers swift différents) mais doit être évitée car IL PEUT CONDUIRE À DES COMPORTEMENTS INATTENDUS (Y COMPRIS REMARQUE: CECI NE FONCTIONNE PAS AVEC XCODE 6.3 BETA 3, SWIFT 1.2

 class Chassis {} class RacingChassis : Chassis {} class Car { var chassis:Chassis? = Chassis() } class RaceCar: Car { override var chassis: RacingChassis? { get { return super.chassis as? RacingChassis } set { super.chassis = newValue } } } 

Théoriquement, vous êtes autorisé à le faire de cette façon …

 class ViewController { var view: UIView! { return _view } private var _view: UIView! } class ScrollView : UIView {} class ScrollViewController : ViewController { override var view: ScrollView! { return super.view as ScrollView! } } class HomeView : ScrollView {} class HomeViewController : ScrollViewController { override var view: HomeView! { return super.view as HomeView! } } 

Cela fonctionne parfaitement dans un terrain de jeu Xcode.

Mais si vous essayez ceci dans un projet réel, une erreur de compilation vous indique:

La “vue” de la déclaration ne peut pas remplacer plus d’une déclaration de superclasse

Je n’ai vérifié que Xcode 6.0 GM dès maintenant.

Malheureusement, vous devrez attendre qu’Apple corrige cela.

J’ai aussi soumis un rapport de bogue. 18518795

J’ai vu beaucoup de raisons pour lesquelles la conception d’une API utilisant des variables plutôt que des fonctions est problématique et pour moi, utiliser des propriétés calculées ressemble à une solution de contournement. Il y a de bonnes raisons de garder vos variables d’instance encapsulées. Ici, j’ai créé un protocole automobile que la voiture est conforme à. Ce protocole a une méthode d’accesseur qui renvoie un object Chassis. Comme la voiture est conforme, la sous-classe RaceCar peut la remplacer et renvoyer une sous-classe de châssis différente. Cela permet à la classe Car de programmer sur une interface (Automobile) et la classe RaceCar qui connaît RacingChassis peut accéder directement à la variable _racingChassis.

 class Chassis {} class RacingChassis: Chassis {} protocol Automobile { func chassis() -> Chassis } class Car: Automobile { private var _chassis: Chassis init () { _chassis = Chassis() } func chassis() -> Chassis { return _chassis } } class RaceCar: Car { private var _racingChassis: RacingChassis override init () { _racingChassis = RacingChassis() super.init() } override func chassis() -> Chassis { return _racingChassis } } 

Un autre exemple expliquant pourquoi la conception d’une API utilisant des variables est interrompue est lorsque vous avez des variables dans un protocole. Si vous souhaitez diviser toutes les fonctions de protocole en une extension, vous pouvez, sauf que les propriétés stockées ne peuvent pas être placées dans des extensions et doivent être définies dans la classe (pour que cela comstack, vous devrez décommenter le code dans Classe AdaptableViewController et supprimer la variable de mode de l’extension):

 protocol Adaptable { var mode: Int { get set } func adapt() } class AdaptableViewController: UIViewController { // var mode = 0 } extension AdaptableViewController: Adaptable { var mode = 0 // comstackr error func adapt() { //TODO: add adapt code } } 

Le code ci-dessus aura cette erreur de compilation: “Les extensions peuvent ne pas avoir de propriétés stockées”. Voici comment vous pouvez réécrire l’exemple ci-dessus afin que tout dans le protocole puisse être séparé dans l’extension en utilisant plutôt des fonctions:

 protocol Adaptable { func mode() -> Int func adapt() } class AdaptableViewController: UIViewController { } extension AdaptableViewController: Adaptable { func mode() -> Int { return 0 } func adapt() { // adapt code } } 

Essaye ça:

 class Chassis {} class RacingChassis : Chassis {} class SuperChassis : RacingChassis {} class Car { private var chassis: Chassis? = nil func getChassis() -> Chassis? { return chassis } func setChassis(chassis: Chassis) { self.chassis = chassis } } class RaceCar: Car { private var chassis: RacingChassis { get { return getChassis() as! RacingChassis } set { setChassis(chassis: newValue) } } override init() { super.init() chassis = RacingChassis() } } class SuperCar: RaceCar { private var chassis: SuperChassis { get { return getChassis() as! SuperChassis } set { setChassis(chassis: newValue) } } override init() { super.init() chassis = SuperChassis() } } 

Il suffit de définir une nouvelle propriété imageview avec une convention de dénomination différente, comme imgview, car imageView est déjà sa propre propriété et nous ne pouvons pas atsortingbuer 2 propriétés fortes.

Vous pouvez y arriver avec l’utilisation de génériques:

 class Descriptor { let var1 = "a" } class OtherDescriptor: Descriptor { let var2 = "b" } class Asset { let descriptor: D init(withDescriptor descriptor: D) { self.descriptor = descriptor } func printInfo() { print(descriptor.var1) } } class OtherAsset: Asset { override func printInfo() { print(descriptor.var1, descriptor.var2) } } let asset = Asset(withDescriptor: Descriptor()) asset.printInfo() // a let otherAsset = OtherAsset(withDescriptor: OtherDescriptor()) otherAsset.printInfo() // ab 

Avec cette approche, vous obtiendrez un code sécurisé de type 100% sans forcer le dépliage.

Mais que cela soit un peu un piratage, et si vous devez redéfinir plusieurs propriétés, vos déclarations de classe ressembleront à un désordre total. Alors faites attention à cette approche.

 class Chassis {} class RacingChassis : Chassis {} class Car { fileprivate let theChassis: Chassis var chassis: Chassis { get { return theChassis } } fileprivate init(_ chassis: Chassis) { theChassis = chassis } convenience init() { self.init(Chassis()) } } class RaceCar: Car { override var chassis: RacingChassis { get { return theChassis as! RacingChassis } } init() { super.init(RacingChassis()) } } 

Vous pouvez simplement créer une autre variable de RacingChassis.

 class Chassis {} class RacingChassis : Chassis {} class Car { let chassis: Chassis init(){ chassis = Chassis() }} class RaceCar: Car { let raceChassis: RacingChassis init(){ raceChassis = RacingChassis() }}