Die vorhandenen Antworten erzählen nur die Hälfte der convenience
Geschichte. Die andere Hälfte der Geschichte, die Hälfte, die keine der vorhandenen Antworten abdeckt, beantwortet die Frage, die Desmond in den Kommentaren gestellt hat:
Warum sollte Swift mich zwingen, mich convenience
vor meinen Initialisierer zu stellen, nur weil ich von dort aus anrufen self.init
muss? "
Ich habe es in dieser Antwort , in der ich einige der Initialisierungsregeln von Swift ausführlich behandele, leicht angesprochen , aber das Hauptaugenmerk lag dort auf dem required
Wort. Aber diese Antwort sprach immer noch etwas an, das für diese Frage und diese Antwort relevant ist. Wir müssen verstehen, wie die Vererbung von Swift-Initialisierern funktioniert.
Da Swift keine nicht initialisierten Variablen zulässt, wird nicht garantiert, dass Sie alle (oder einige) Initialisierer von der Klasse erben, von der Sie erben. Wenn wir unserer Unterklasse Unterklassen hinzufügen und nicht initialisierte Instanzvariablen hinzufügen, haben wir aufgehört, Initialisierer zu erben. Und bis wir eigene Initialisierer hinzufügen, wird der Compiler uns anschreien.
Eine nicht initialisierte Instanzvariable ist eine Instanzvariable, der kein Standardwert zugewiesen wurde (wobei zu berücksichtigen ist, dass Optionals und implizit entpackte Optionals automatisch einen Standardwert von annehmen nil
).
Also in diesem Fall:
class Foo {
var a: Int
}
a
ist eine nicht initialisierte Instanzvariable. Dies wird nur kompiliert, wenn wir a
einen Standardwert angeben:
class Foo {
var a: Int = 0
}
oder a
in einer Initialisierungsmethode initialisieren:
class Foo {
var a: Int
init(a: Int) {
self.a = a
}
}
Nun wollen wir sehen, was passiert, wenn wir eine Unterklasse Foo
bilden.
class Bar: Foo {
var b: Int
init(a: Int, b: Int) {
self.b = b
super.init(a: a)
}
}
Richtig? Wir haben eine Variable hinzugefügt und einen Initialisierer hinzugefügt, um einen Wert festzulegen, b
damit er kompiliert wird. Abhängig davon, aus welcher Sprache Sie kommen, können Sie erwarten, dass Bar
der Foo
Initialisierer geerbt wurde init(a: Int)
. Aber das tut es nicht. Und wie könnte es? Wie funktioniert Foo
‚s init(a: Int)
Know - how einen Wert an die zuweisen b
Variable, die Bar
hinzugefügt? Das tut es nicht. Daher können wir eine Bar
Instanz nicht mit einem Initialisierer initialisieren, der nicht alle unsere Werte initialisieren kann.
Was hat das alles damit zu tun convenience
?
Schauen wir uns die Regeln für die Initialisierungsvererbung an :
Regel 1
Wenn Ihre Unterklasse keine festgelegten Initialisierer definiert, erbt sie automatisch alle von der Oberklasse festgelegten Initialisierer.
Regel 2
Wenn Ihre Unterklasse eine Implementierung aller von der Oberklasse festgelegten Initialisierer bereitstellt - entweder durch Erben gemäß Regel 1 oder durch Bereitstellen einer benutzerdefinierten Implementierung als Teil ihrer Definition -, erbt sie automatisch alle Superklassen-Komfortinitialisierer.
Beachten Sie Regel 2, in der Convenience-Initialisierer erwähnt werden.
Das convenience
Schlüsselwort gibt uns also an, welche Initialisierer von Unterklassen geerbt werden können, die Instanzvariablen ohne Standardwerte hinzufügen.
Nehmen wir diese Beispielklasse Base
:
class Base {
let a: Int
let b: Int
init(a: Int, b: Int) {
self.a = a
self.b = b
}
convenience init() {
self.init(a: 0, b: 0)
}
convenience init(a: Int) {
self.init(a: a, b: 0)
}
convenience init(b: Int) {
self.init(a: 0, b: b)
}
}
Beachten Sie, dass wir hier drei convenience
Initialisierer haben. Das heißt, wir haben drei Initialisierer, die vererbt werden können. Und wir haben einen bestimmten Initialisierer (ein bestimmter Initialisierer ist einfach ein beliebiger Initialisierer, der kein praktischer Initialisierer ist).
Wir können Instanzen der Basisklasse auf vier verschiedene Arten instanziieren:
Erstellen wir also eine Unterklasse.
class NonInheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
}
Wir erben von Base
. Wir haben unsere eigene Instanzvariable hinzugefügt und keinen Standardwert angegeben, daher müssen wir unsere eigenen Initialisierer hinzufügen. Wir haben einen hinzugefügt init(a: Int, b: Int, c: Int)
, der jedoch nicht mit der Signatur des von der Base
Klasse festgelegten Initialisierers übereinstimmt : init(a: Int, b: Int)
. Das heißt, wir erben keine Initialisierer von Base
:
Was würde also passieren, wenn wir von erben würden Base
, aber wir haben einen Initialisierer implementiert, der mit dem festgelegten Initialisierer von übereinstimmt Base
?
class Inheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
convenience override init(a: Int, b: Int) {
self.init(a: a, b: b, c: 0)
}
}
Zusätzlich zu den beiden Initialisierern, die wir direkt in dieser Klasse implementiert haben Base
, können wir jetzt alle Initialisierer der Base
Klasse erben , da wir einen Initialisierer implementiert haben, der dem festgelegten Initialisierer der Klasse entspricht convenience
:
Die Tatsache, dass der Initialisierer mit der passenden Signatur als markiert convenience
ist, spielt hier keine Rolle . Dies bedeutet nur, dass Inheritor
nur ein Initialisierer festgelegt ist. Wenn wir also von erben Inheritor
, müssen wir nur diesen einen festgelegten Initialisierer implementieren, und dann erben wir den Inheritor
praktischen Initialisierer, was wiederum bedeutet, dass wir alle Base
festgelegten Initialisierer implementiert haben und seine convenience
Initialisierer erben können .