Betrachten Sie die zwei 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 festgelegte Initialisierer jeder Klasse mit allen erforderlichen Werten aufgerufen. Warum muss ich mich also in B
s init
wiederholen, indem ich wieder einen Standardwert für x
angibt, wenn die Bequemlichkeit init
in A
gut funktioniert?
Dies ist Regel 1 der "Initializer Chaining" -Regeln, wie im Swift-Programmierhandbuch angegeben, die wie folgt lautet:
Regel 1: Designated -Initialisierer müssen einen design -Initialisierer von ihrem .__ aufrufen. sofortige Superklasse.
Betonung meiner. Designierte Initialisierer können keine komfortablen Initialisierer aufrufen.
Es gibt ein Diagramm, das mit den Regeln übereinstimmt, um zu zeigen, welche "Anweisungen" für die Initialisierung zulässig sind:
Erwägen
class A
{
var a: Int
var b: Int
init (a: Int, b: Int) {
print("Entering A.init(a,b)")
self.a = a; self.b = b
}
convenience init(a: Int) {
print("Entering A.init(a)")
self.init(a: a, b: 0)
}
convenience init() {
print("Entering A.init()")
self.init(a:0)
}
}
class B : A
{
var c: Int
override init(a: Int, b: Int)
{
print("Entering B.init(a,b)")
self.c = 0; super.init(a: a, b: b)
}
}
var b = B()
Da alle angegebenen Initialisierer der Klasse A überschrieben werden, erbt die Klasse B alle praktischen Initialisierer von A. Die Ausführung dieses Befehls wird ausgegeben
Entering A.init()
Entering A.init(a:)
Entering B.init(a:,b:)
Entering A.init(a:,b:)
Wenn nun der festgelegte Initialisierer B.init (a: b :) den Bequemlichkeitsinitialisierer A.init (a :) der Basisklasse aufrufen darf, würde dies zu einem rekursiven Aufruf von B.init (a:, b: ).
Es liegt daran, dass Sie mit einer unendlichen Rekursion enden können. Erwägen:
class SuperClass {
init() {
}
convenience init(value: Int) {
// calls init() of the current class
// so init() for SubClass if the instance
// is a SubClass
self.init()
}
}
class SubClass : SuperClass {
override init() {
super.init(value: 10)
}
}
und schau mal:
let a = SubClass()
das wird SubClass.init()
aufrufen, das SuperClass.init(value:)
aufrufen wird, das SubClass.init()
aufruft.
Die festgelegten/Convenience-Init-Regeln sind so ausgelegt, dass eine Klasseninitialisierung immer korrekt ist.
Ich habe dafür eine Arbeit gefunden. Es ist nicht sehr 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 Superklasse mit der Bequemlichkeit init
rechts in der init
der Unterklasse zu erstellen. Dann rufen Sie die bezeichnete init
des Supers mit der gerade erstellten Instanz auf.
class A {
var x: Int
init(x: Int) {
self.x = x
}
convenience init() {
self.init(x: 0)
}
}
class B: A {
init() {
// calls A's convenience init, gets instance of A with default x value
let intermediate = A()
super.init(x: intermediate.x)
}
}
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. Wenn ich es verstanden habe, bedenke folgendes:
class Dragon {
var legs: Int
var isFlying: Bool
init(legs: Int, isFlying: Bool) {
self.legs = legs
self.isFlying = isFlying
}
convenience initWyvern() {
self.init(legs: 2, isFlying: true)
}
}
Aber betrachten wir eine Wyrm-Unterklasse: Ein Wyrm ist ein Drache ohne Beine und Flügel. Der Initializer für einen Wyvern (2 Beine, 2 Flügel) ist also falsch! Dieser Fehler kann vermieden werden, wenn der Bequemlichkeits-Wyvern-Initializer einfach nicht aufgerufen werden kann, sondern nur der vollständig bestimmte Initializer:
class Wyrm: Dragon {
init() {
super.init(legs: 0, isFlying: false)
}
}
Erwägen Sie, den Initialisierungscode aus Ihrer praktischen init()
in eine neue Hilfsfunktion foo()
zu extrahieren. Rufen Sie foo(...)
auf, um die Initialisierung in Ihrer Unterklasse durchzuführen.