Ich habe zwei Klassen Shape
undSquare
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
}
}
Bei der obigen Implementierung erhalte ich den Fehler:
property 'self.sideLength' not initialized at super.init call
super.init(name:name)
Warum muss ich einstellen, self.sideLength
bevor ich anrufe super.init
?
properties
compiler-errors
swift
JuJoDi
quelle
quelle
Antworten:
Zitat aus der Programmiersprache Swift, das Ihre Frage beantwortet:
quelle
init
.Swift hat eine sehr klare, spezifische Abfolge von Operationen, die in Initialisierern ausgeführt werden. Beginnen wir mit einigen grundlegenden Beispielen und arbeiten uns bis zu einem allgemeinen Fall vor.
Nehmen wir ein Objekt A. Wir definieren es wie folgt.
Beachten Sie, dass A keine Superklasse hat und daher keine super.init () -Funktion aufrufen kann, da diese nicht vorhanden ist.
OK, jetzt wollen wir die Unterklasse A mit einer neuen Klasse namens B unterteilen.
Dies ist eine Abweichung von Objective-C, wo
[super init]
normalerweise zuerst vor allem anderen aufgerufen wird. Nicht so bei Swift. Sie sind dafür verantwortlich, sicherzustellen, dass sich Ihre Instanzvariablen in einem konsistenten Zustand befinden, bevor Sie etwas anderes tun, einschließlich der Aufrufmethoden (einschließlich des Initialisierers Ihrer Oberklasse).quelle
Aus den Dokumenten
Warum brauchen wir so eine Sicherheitsüberprüfung?
Um dies zu beantworten, lassen Sie uns den Initialisierungsprozess schnell durchlaufen.
Um sicherzustellen, dass der zweistufige Initialisierungsprozess wie oben definiert durchgeführt wird, gibt es vier Sicherheitsüberprüfungen. Eine davon ist:
Bei der zweiphasigen Initialisierung geht es nie um die Reihenfolge, aber diese Sicherheitsüberprüfung führt
super.init
dazu, dass nach der Initialisierung aller Eigenschaften eine Bestellung durchgeführt wird.Die Sicherheitsüberprüfung 1 scheint irrelevant zu sein, da die zweiphasige Initialisierung verhindert, dass auf Eigenschaftswerte zugegriffen werden kann, bevor sie initialisiert werden, ohne diese Sicherheitsüberprüfung 1 erfüllt werden kann.
Wie in diesem Beispiel
Triangle.init
hat jede Eigenschaft vor der Verwendung initialisiert. Sicherheitscheck 1 scheint also irrelevant,Aber dann könnte es ein anderes Szenario geben, ein bisschen komplex,
Ausgabe :
Wenn wir das
super.init
vor dem Setzen des aufgerufen hättenhypotenuse
, hätte dersuper.init
Aufruf dann das aufgerufen,printShapeDescription()
und da dies überschrieben wurde, würde er zuerst auf die Implementierung der Triangle-Klasse von zurückgreifenprintShapeDescription()
. DieprintShapeDescription()
of Triangle-Klasse greift aufhypotenuse
eine nicht optionale Eigenschaft zu, die noch nicht initialisiert wurde. Dies ist nicht zulässig, da durch die zweiphasige Initialisierung verhindert wird, dass auf Eigenschaftswerte zugegriffen wird, bevor sie initialisiert werdenStellen Sie also sicher, dass die Zwei-Phasen-Initialisierung wie definiert durchgeführt wird. Es muss eine bestimmte
super.init
Aufrufreihenfolge geben. Nach der Initialisierung aller von derself
Klasse eingeführten Eigenschaften benötigen wir daher eine Sicherheitsüberprüfung 1quelle
init
möglicherweise eine (Überschreibungs-) Funktion aufruft ... in der diese Funktion auf die Unterklasseneigenschaft zugreift. Um zu vermeiden, dass keine Werte festgelegt werden, muss der Aufruf vonsuper
erfolgen, nachdem alle Werte festgelegt wurden. OK macht Sinn. Frage mich, wie Objective-C es damals gemacht hat und warum Siesuper
zuerst anrufen mussten ?printShapeDescription()
vor,self.sides = sides; self.name = named;
was diesen Fehler erzeugen würde :use of 'self' in method call 'printShapeDescription' before all stored properties are initialized
. Der Fehler des OP wird angegeben, um die Möglichkeit eines Laufzeitfehlers zu verringern.printShapeDescription
es sich um eine Funktion handelte, die sich nicht aufself
"Drucken" ("nichts") bezog, hätte es keine Probleme gegeben. (Aber selbst dafür würde der Compiler einen Fehler auslösen, weil es nicht super klug ist)Die "super.init ()" sollte aufgerufen werden, nachdem Sie alle Ihre Instanzvariablen initialisiert haben.
In Apples "Intermediate Swift" -Video (Sie finden es auf der Apple Developer-Video-Ressourcenseite https://developer.apple.com/videos/wwdc/2014/ ) wird gegen 28:40 Uhr ausdrücklich angegeben, dass alle Initialisierer in Die Superklasse muss aufgerufen werden, nachdem Sie Ihre Instanzvariablen initialisiert haben.
In Objective-C war es umgekehrt. Da in Swift alle Eigenschaften initialisiert werden müssen, bevor sie verwendet werden, müssen wir zuerst die Eigenschaften initialisieren. Dies soll verhindern, dass die "init ()" - Methode der Superklasse die aufgerufene Funktion aufruft, ohne zuvor die Eigenschaften zu initialisieren.
Die Implementierung von "Square" sollte also sein:
quelle
Entschuldigung für die hässliche Formatierung. Setzen Sie einfach ein Fragezeichen nach der Deklaration und alles wird in Ordnung sein. Eine Frage teilt dem Compiler mit, dass der Wert optional ist.
Edit1:
Es gibt eine bessere Möglichkeit, diesen Fehler zu überspringen. Laut dem Kommentar von jmaschad gibt es in Ihrem Fall keinen Grund, optional zu verwenden, da optionale Optionen nicht bequem zu verwenden sind und Sie immer überprüfen müssen, ob optional nicht null ist, bevor Sie darauf zugreifen. Alles was Sie tun müssen, ist das Mitglied nach der Deklaration zu initialisieren:
Edit2:
Nachdem zwei Minuten auf diese Antwort gekommen waren, fand ich einen noch besseren Weg. Wenn Sie möchten, dass das Klassenmitglied in Ihrem Konstruktor initialisiert wird, müssen Sie ihm im Konstruktor und vor dem Aufruf von super.init () einen Anfangswert zuweisen. So was:
Viel Glück beim Lernen von Swift.
quelle
super.init(name:name)
undself.sideLength = sideLength
. Das DeklarierensideLength
als optional ist falsch und führt später zu zusätzlichen Problemen, wenn Sie das Auspacken erzwingen müssen.var sideLength: Double
, keine Notwendigkeit, ihm einen Anfangswert zuzuweisenswift erzwingt, dass Sie jede Mitgliedsvariable initialisieren, bevor sie jemals verwendet wird / werden könnte. Da es nicht sicher sein kann, was passiert, wenn es an der Reihe ist, tritt ein Fehler auf: Besser sicher als leid
quelle
super.init
in die erste Zeile schreiben ?Edward,
Sie können den Code in Ihrem Beispiel folgendermaßen ändern:
Dies verwendet eine implizit entpackte Option.
In der Dokumentation können wir lesen:
quelle
Mit Swift können Sie keine Superklasse initialisieren, ohne die Eigenschaften zu initialisieren, umgekehrt zu Obj C. Sie müssen also alle Eigenschaften initialisieren, bevor Sie "super.init" aufrufen.
Bitte gehen Sie zu http://blog.scottlogic.com/2014/11/20/swift-initialisation.html . Es gibt eine schöne Erklärung für Ihr Problem.
quelle
Fügen Sie am Ende der Deklaration null hinzu.
Dies funktionierte für meinen Fall, aber möglicherweise nicht für Ihren
quelle
Sie starten nur in der falschen Reihenfolge.
quelle
Ich werde wahrscheinlich einige Abstimmungen erhalten, aber um ehrlich zu sein, ist das Leben auf diese Weise einfacher:
quelle
Es ist ein erstaunlich dummes Design.
Betrachten Sie so etwas:
Dies ist ungültig, wie oben erwähnt. Aber so ist:
Weil 'Selbst' nicht initialisiert wurde.
Ich hoffe aufrichtig, dass dieser Fehler bald behoben ist.
(Ja, ich weiß, ich könnte ein leeres Objekt erstellen und dann die Größe einstellen, aber das ist einfach dumm).
quelle