Da Swift das Überladen von Methoden und Initialisierern unterstützt, können Sie mehrere init
nebeneinander stellen und das verwenden, was Sie für zweckmäßig halten:
class Person {
var name:String
init(name: String) {
self.name = name
}
init() {
self.name = "John"
}
}
Warum sollte es überhaupt ein convenience
Keyword geben? Was macht das Folgende wesentlich besser?
class Person {
var name:String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "John")
}
}
swift
initialization
Desmond Hume
quelle
quelle
Antworten:
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: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:
a
ist eine nicht initialisierte Instanzvariable. Dies wird nur kompiliert, wenn wira
einen Standardwert angeben:oder
a
in einer Initialisierungsmethode initialisieren:Nun wollen wir sehen, was passiert, wenn wir eine Unterklasse
Foo
bilden.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, dassBar
derFoo
Initialisierer geerbt wurdeinit(a: Int)
. Aber das tut es nicht. Und wie könnte es? Wie funktioniertFoo
‚sinit(a: Int)
Know - how einen Wert an die zuweisenb
Variable, dieBar
hinzugefügt? Das tut es nicht. Daher können wir eineBar
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 :
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
: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.
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ügtinit(a: Int, b: Int, c: Int)
, der jedoch nicht mit der Signatur des von derBase
Klasse festgelegten Initialisierers übereinstimmt :init(a: Int, b: Int)
. Das heißt, wir erben keine Initialisierer vonBase
:Was würde also passieren, wenn wir von erben würden
Base
, aber wir haben einen Initialisierer implementiert, der mit dem festgelegten Initialisierer von übereinstimmtBase
?Zusätzlich zu den beiden Initialisierern, die wir direkt in dieser Klasse implementiert haben
Base
, können wir jetzt alle Initialisierer derBase
Klasse erben , da wir einen Initialisierer implementiert haben, der dem festgelegten Initialisierer der Klasse entsprichtconvenience
:Die Tatsache, dass der Initialisierer mit der passenden Signatur als markiert
convenience
ist, spielt hier keine Rolle . Dies bedeutet nur, dassInheritor
nur ein Initialisierer festgelegt ist. Wenn wir also von erbenInheritor
, müssen wir nur diesen einen festgelegten Initialisierer implementieren, und dann erben wir denInheritor
praktischen Initialisierer, was wiederum bedeutet, dass wir alleBase
festgelegten Initialisierer implementiert haben und seineconvenience
Initialisierer erben können .quelle
init(a: Int)
verlassen würdeb
nicht initialisierten.Meistens Klarheit. Aus Ihrem zweiten Beispiel:
ist erforderlich oder bezeichnet . Es muss alle Ihre Konstanten und Variablen initialisieren. Komfortinitialisierer sind optional und können normalerweise verwendet werden, um die Initialisierung zu vereinfachen. Angenommen, Ihre Personenklasse hat ein optionales variables Geschlecht:
wo Geschlecht eine Aufzählung ist
Sie könnten Convenience-Initialisierer wie diesen haben
Convenience-Initialisierer müssen die darin angegebenen oder erforderlichen Initialisierer aufrufen . Wenn Ihre Klasse eine Unterklasse ist, muss sie
super.init()
innerhalb ihrer Initialisierung aufgerufen werden.quelle
convenience
Schlüsselwort zu tun versuche, aber Swift würde immer noch Fehler machen. Das ist nicht die Art von Einfachheit, die ich von Apple erwartet hatte =)Nun, als erstes fällt mir ein, dass es bei der Klassenvererbung für die Codeorganisation und Lesbarkeit verwendet wird. Stellen Sie sich
Person
ein solches Szenario vor, wenn Sie mit Ihrer Klasse fortfahrenMit dem praktischen Initialisierer kann ich ein
Employee()
Objekt ohne Wert erstellen , daher das Wortconvenience
quelle
convenience
Würde Swift mit weggenommenen Schlüsselwörtern nicht genug Informationen erhalten, um sich genauso zu verhalten?convenience
Schlüsselwort entfernen , können Sie dasEmployee
Objekt nicht ohne Argument initialisieren .Employee()
ruft der Aufruf den (geerbten, aufgrund vonconvenience
) Initialisierer aufinit()
, der aufruftself.init(name: "Unknown")
.init(name: String)
, auch ein praktischer Initialisierer fürEmployee
, ruft den angegebenen Initialisierer auf.Abgesehen von den Punkten, die andere Benutzer hier erklärt haben, ist mein Verständnis.
Ich spüre stark die Verbindung zwischen Convenience-Initialisierer und Erweiterungen. Convenience-Initialisierer sind für mich am nützlichsten, wenn ich die Initialisierung einer vorhandenen Klasse ändern (in den meisten Fällen kurz oder einfach machen) möchte.
Beispielsweise hat eine von Ihnen verwendete Klasse eines Drittanbieters
init
vier Parameter, aber in Ihrer Anwendung haben die letzten beiden den gleichen Wert. Um mehr Eingabe zu vermeiden und Ihren Code sauber zu machen, können Sie einenconvenience init
mit nur zwei Parametern definieren und darinself.init
den Parameter last to to mit Standardwerten aufrufen .quelle
convenience
vor meinen Initialisierer zu stellen, nur weil ich von dort aus anrufenself.init
muss? Dies scheint überflüssig und irgendwie unpraktisch.Gemäß der Swift 2.1-Dokumentation müssen
convenience
Initialisierer bestimmte Regeln einhalten:Ein
convenience
Initialisierer kann nur Intializer in derselben Klasse aufrufen, nicht in Superklassen (nur über, nicht über)Ein
convenience
Initialisierer muss irgendwo in der Kette einen bestimmten Initialisierer aufrufenEin
convenience
Initialisierer kann KEINE Eigenschaft ändern, bevor er einen anderen Initialisierer aufgerufen hat - während ein bestimmter Initialisierer Eigenschaften initialisieren muss, die von der aktuellen Klasse eingeführt werden, bevor ein anderer Initialisierer aufgerufen wird .Durch die Verwendung des
convenience
Schlüsselworts weiß der Swift-Compiler, dass er nach diesen Bedingungen suchen muss - sonst könnte er nicht.quelle
convenience
Schlüsselwort klären .let
Eigenschaften ). Eigenschaften können nicht initialisiert werden. Ein designierter Initialisierer hat die Verantwortung, alle eingeführten Eigenschaften zusuper
initialisieren, bevor er einen designierten Initialisierer aufruft .Eine Klasse kann mehr als einen festgelegten Initialisierer haben. Ein Convenience-Initialisierer ist ein sekundärer Initialisierer, der einen bestimmten Initialisierer derselben Klasse aufrufen muss.
quelle