J'ai deux classes, Shape
etSquare
class Shape {
var numberOfSides = 0
var name: String
init(name:String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
super.init(name:name) // Error here
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Avec l'implémentation ci-dessus, j'obtiens l'erreur:
property 'self.sideLength' not initialized at super.init call
super.init(name:name)
Pourquoi dois-je régler self.sideLength
avant d'appeler super.init
?
properties
compiler-errors
swift
JuJoDi
la source
la source
Réponses:
Citation du langage de programmation Swift, qui répond à votre question:
la source
init
.Swift a une séquence d'opérations très claire et spécifique qui se fait dans les initialiseurs. Commençons par quelques exemples de base et progressons jusqu'à un cas général.
Prenons un objet A. Nous le définirons comme suit.
Notez que A n'a pas de superclasse, il ne peut donc pas appeler une fonction super.init () car elle n'existe pas.
OK, alors maintenant, sous-classe A avec une nouvelle classe nommée B.
Il s'agit d'une dérogation à Objective-C, qui
[super init]
serait généralement appelée en premier avant toute autre chose. Ce n'est pas le cas à Swift. Vous êtes responsable de vous assurer que vos variables d'instance sont dans un état cohérent avant de faire quoi que ce soit d'autre, y compris les méthodes d'appel (qui inclut l'initialiseur de votre superclasse).la source
À partir des documents
Pourquoi avons-nous besoin d'un contrôle de sécurité comme celui-ci?
Pour répondre à cela, nous allons parcourir le processus d'initialisation en un rien de temps.
Donc, pour vous assurer que le processus d'initialisation en deux étapes est effectué comme défini ci-dessus, il y a quatre contrôles de sécurité, l'un d'eux est,
Or, l'initialisation en deux phases ne parle jamais d'ordre, mais ce contrôle de sécurité, introduit
super.init
à ordonner, après l'initialisation de toutes les propriétés.Le contrôle de sécurité 1 peut sembler non pertinent, car l' initialisation en deux phases empêche d'accéder aux valeurs des propriétés avant qu'elles ne soient initialisées , sans ce contrôle de sécurité 1.
Comme dans cet exemple
Triangle.init
a initialisé, chaque propriété avant d'être utilisée. Donc, le contrôle de sécurité 1 ne semble pas pertinent,Mais il pourrait y avoir un autre scénario, un peu complexe,
Production :
Ici, si nous avions appelé
super.init
avant de définir lehypotenuse
, l'super.init
appel aurait alors appelé leprintShapeDescription()
et puisque cela a été remplacé, il reviendrait d'abord à l'implémentation de la classe TriangleprintShapeDescription()
. LaprintShapeDescription()
classe of Triangle accède àhypotenuse
une propriété non facultative qui n'a pas encore été initialisée. Et cela n'est pas autorisé car l' initialisation en deux phases empêche l'accès aux valeurs des propriétés avant leur initialisationAssurez-vous donc que l'initialisation en deux phases se fait comme défini, il doit y avoir un ordre spécifique d'appel
super.init
, et c'est-à-dire, après avoir initialisé toutes les propriétés introduites parself
classe, nous avons donc besoin d'un contrôle de sécurité 1la source
init
peut appeler une fonction (remplacée) ... dans laquelle cette fonction accède à la propriété des sous-classes, puis pour éviter de ne pas avoir de valeurs définies, l'appel àsuper
doit se produire après que toutes les valeurs sont définies. OK a du sens. Vous vous demandez comment Objective-C a fait alors et pourquoi vous avez dû appeler ensuper
premier?printShapeDescription()
avantself.sides = sides; self.name = named;
qui générerait cette erreur:use of 'self' in method call 'printShapeDescription' before all stored properties are initialized
. L'erreur de l'OP est donnée pour réduire la «possibilité» d'erreur d'exécution.printShapeDescription
c'était une fonction qui ne faisait pas référence,self
c'est- à- dire quelque chose comme «print» («rien»), il n'y aurait eu aucun problème. (Pourtant, même pour cela, le compilateur lancerait une erreur, car ce n'est pas super intelligent)Le "super.init ()" doit être appelé après avoir initialisé toutes vos variables d'instance.
Dans la vidéo "Intermediate Swift" d'Apple (vous pouvez la trouver sur la page de ressources vidéo pour les développeurs Apple https://developer.apple.com/videos/wwdc/2014/ ), vers 28 h 40, il est dit explicitement que tous les initialiseurs de la super classe doit être appelée APRÈS avoir initialisé vos variables d'instance.
Dans Objective-C, c'était l'inverse. Dans Swift, puisque toutes les propriétés doivent être initialisées avant d'être utilisées, nous devons d'abord initialiser les propriétés. Ceci est destiné à empêcher un appel à une fonction redéfinie à partir de la méthode "init ()" de la super classe, sans initialiser les propriétés au préalable.
La mise en œuvre de "Square" devrait donc être:
la source
Désolé pour le formatage moche. Mettez simplement un caractère de question après la déclaration et tout ira bien. Une question indique au compilateur que la valeur est facultative.
Edit1:
Il existe une meilleure façon d'ignorer cette erreur. Selon le commentaire de jmaschad, il n'y a aucune raison d'utiliser facultatif dans votre cas car les optionnels ne sont pas confortables à utiliser et vous devez toujours vérifier si facultatif n'est pas nul avant d'y accéder. Il vous suffit donc d'initialiser le membre après la déclaration:
Edit2:
Après deux inconvénients à cette réponse, j'ai trouvé un moyen encore meilleur. Si vous voulez que le membre de la classe soit initialisé dans votre constructeur, vous devez lui attribuer une valeur initiale à l'intérieur de contructor et avant l'appel de super.init (). Comme ça:
Bonne chance pour apprendre Swift.
la source
super.init(name:name)
etself.sideLength = sideLength
. DéclarersideLength
comme facultatif est erroné et introduit plus de tracas plus tard lorsque vous devez forcer le déballage.var sideLength: Double
, pas besoin de lui affecter une valeur initialeswift vous oblige à initialiser chaque var membre avant qu'il ne soit / ne soit jamais utilisé. Puisqu'il ne peut pas être sûr de ce qui se passe quand c'est le tour, il se trompe: mieux vaut prévenir que guérir
la source
super.init
dans la première ligne?Edward,
Vous pouvez modifier le code dans votre exemple comme ceci:
Ceci utilise une option implicitement déballée.
Dans la documentation, nous pouvons lire:
la source
Swift ne vous permettra pas d'initialiser une super classe sans initialiser les propriétés, à l'inverse de l'Obj C. Vous devez donc initialiser toutes les propriétés avant d'appeler "super.init".
Veuillez vous rendre sur http://blog.scottlogic.com/2014/11/20/swift-initialisation.html . Il donne une belle explication à votre problème.
la source
Ajoutez zéro à la fin de la déclaration.
Cela a fonctionné pour mon cas, mais peut ne pas fonctionner pour le vôtre
la source
Vous commencez juste dans le mauvais ordre.
la source
Je vais probablement recevoir des downvotes, mais pour être honnête, la vie est plus facile de cette façon:
la source
C'est une conception incroyablement stupide.
Considérez quelque chose comme ceci:
Ceci n'est pas valide, comme indiqué ci-dessus. Mais il en va de même:
Parce que «soi» n'a pas été initialisé.
J'espère sincèrement que ce bug sera bientôt corrigé.
(Oui, je sais que je pourrais créer un objet vide, puis définir la taille, mais c'est juste stupide).
la source