Die vorhandenen Antworten erzählen nur die Hälfte der convenienceGeschichte. 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 conveniencevor meinen Initialisierer zu stellen, nur weil ich von dort aus anrufen self.initmuss? "
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 requiredWort. 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
}
aist eine nicht initialisierte Instanzvariable. Dies wird nur kompiliert, wenn wir aeinen Standardwert angeben:
class Foo {
var a: Int = 0
}
oder ain einer Initialisierungsmethode initialisieren:
class Foo {
var a: Int
init(a: Int) {
self.a = a
}
}
Nun wollen wir sehen, was passiert, wenn wir eine Unterklasse Foobilden.
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, bdamit er kompiliert wird. Abhängig davon, aus welcher Sprache Sie kommen, können Sie erwarten, dass Barder FooInitialisierer 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 bVariable, die Barhinzugefügt? Das tut es nicht. Daher können wir eine BarInstanz 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 convenienceSchlü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 convenienceInitialisierer 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 BaseKlasse 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 BaseKlasse 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 convenienceist, spielt hier keine Rolle . Dies bedeutet nur, dass Inheritornur ein Initialisierer festgelegt ist. Wenn wir also von erben Inheritor, müssen wir nur diesen einen festgelegten Initialisierer implementieren, und dann erben wir den Inheritorpraktischen Initialisierer, was wiederum bedeutet, dass wir alle Basefestgelegten Initialisierer implementiert haben und seine convenienceInitialisierer erben können .