Betrachten Sie die beiden Klassen:
class A {
var x: Int
init(x: Int) {
self.x = x
}
convenience init() {
self.init(x: 0)
}
}
class B: A {
init() {
super.init() // Error: Must call a designated initializer of the superclass 'A'
}
}
Ich verstehe nicht, warum das nicht erlaubt ist. Letztendlich wird der von jeder Klasse festgelegte Initialisierer mit allen benötigten Werten aufgerufen. Warum muss ich mich also wiederholen B
, init
indem ich einen Standardwert für x
erneut spezifiziere , wenn die Bequemlichkeit init
in A
gut ist?
class
initialization
swift
Robert
quelle
quelle
Antworten:
Dies ist Regel 1 der Regeln für die "Initializer-Verkettung", wie im Swift-Programmierhandbuch angegeben:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html
Hervorhebung von mir. Bestimmte Initialisierer können keine praktischen Initialisierer aufrufen.
Es gibt ein Diagramm, das den Regeln entspricht, um zu demonstrieren, welche "Anweisungen" des Initialisierers zulässig sind:
quelle
SCNGeometry
: Sie könnenSCNGeometryElement
s nur mit dem Convenience-Initialisierer hinzufügen , daher kann man nicht davon erben.Erwägen
Da alle festgelegten Initialisierer der Klasse A überschrieben werden, erbt die Klasse B alle praktischen Initialisierer von A. Wenn Sie dies ausführen, wird dies ausgegeben
Wenn der angegebene Initialisierer B.init (a: b :) den Basisklassen-Convenience-Initialisierer A.init (a :) aufrufen darf, führt dies zu einem rekursiven Aufruf von B.init (a :, b :. ).
quelle
Es ist, weil Sie mit einer unendlichen Rekursion enden können. Erwägen:
und schau dir an:
was wird anrufen
SubClass.init()
welches anruftSuperClass.init(value:)
welches anruftSubClass.init()
.Die festgelegten / praktischen Initialisierungsregeln sind so konzipiert, dass eine Klasseninitialisierung immer korrekt ist.
quelle
Ich habe eine Lösung dafür gefunden. Es ist nicht besonders hübsch, aber es löst das Problem, die Werte einer Oberklasse nicht zu kennen oder Standardwerte festlegen zu wollen.
Alles, was Sie tun müssen, ist, eine Instanz der Oberklasse zu erstellen, indem Sie die Bequemlichkeit
init
direkt ininit
der Unterklasse verwenden. Dann rufen Sie den designierteninit
Super mit der gerade erstellten Instanz auf.quelle
Ziehen Sie in Betracht, den Initialisierungscode von Ihrer praktischen
init()
zu einer neuen Hilfsfunktion zu extrahierenfoo()
, und rufen Siefoo(...)
auf, um die Initialisierung in Ihrer Unterklasse durchzuführen.quelle
Schauen Sie sich das WWDC-Video "403 Intermediate Swift" um 18:30 an, um eine ausführliche Erklärung der Initialisierer und ihrer Vererbung zu erhalten. Beachten Sie nach meinem Verständnis Folgendes:
Aber jetzt betrachten wir eine Wyrm-Unterklasse: Ein Wyrm ist ein Drache ohne Beine und ohne Flügel. Der Initializer für einen Wyvern (2 Beine, 2 Flügel) ist also falsch! Dieser Fehler kann vermieden werden, wenn der bequeme Wyvern-Initializer einfach nicht aufgerufen werden kann, sondern nur der vollständig festgelegte Initializer:
quelle
initWyvern
erstelle, wenn es Sinn macht, angerufen zu werden?Wyrm
die Anzahl der Beine zu überschreiben, nachdem ein Convenience-Initialisierer aufgerufen wurde.Warum haben Sie nicht einfach zwei Initialisierer - einen mit einem Standardwert?
quelle