Warum initialisiert Swift zuerst die richtigen Felder der Unterklasse?

9

In der Sprache Swift muss man zum Initialisieren einer Instanz alle Felder dieser Klasse ausfüllen und erst dann den Superkonstruktor aufrufen:

class Base {
    var name: String

    init(name: String) {
        self.name = name
    }
}

class Derived: Base {
    var number: Int

    init(name: String, number: Int) {
        // won't compile if interchange lines
        self.number = number
        super.init(name)
    }
}

Für mich scheint es rückwärts zu sein, da die Instanz selfvor dem Zuweisen von Werten zu ihren Feldern erstellt werden muss und dieser Code den Eindruck erweckt, als ob die Verkettung erst nach der Zuweisung erfolgt. Abgesehen davon hat die Oberklasse keine legalen Mittel, um die eingeführten Attribute ihrer Unterklasse zu lesen, sodass die Sicherheit in diesem Fall nicht zählt.

Viele andere Sprachen, wie JavaScript und sogar Objective C, das ein etwas spiritueller Vorfahr von Swift ist, erfordern vor dem Zugriff einen Verkettungsaufruf self, nicht danach.

Was ist der Grund für diese Wahl, dass die Felder definiert werden müssen, bevor der Superkonstruktor aufgerufen wird?

Zomagk
quelle
Interessant. Gibt es eine Einschränkung, bei der Methodenaufrufe (insbesondere virtuelle) platziert werden können, z. B. nur nach dem Verketten? In C # erhalten die Felder einer Unterklasse den Standardwert, dann Verkettung / Superklassenkonstruktion, dann können Sie sie für real IIRC initialisieren.
Erik Eidt
3
Der Punkt ist, dass Sie alle Felder initialisieren müssen, bevor Sie uneingeschränkten Zugriff auf zulassen self.
CodesInChaos
@ErikEidt: In Swift werden nur optionale Felder automatisch auf Null initialisiert.
Gnasher729

Antworten:

9

Wenn Sie in C ++ ein abgeleitetes Objekt erstellen, beginnt es als Basisobjekt, während der Basiskonstruktor ausgeführt wird. Zum Zeitpunkt der Ausführung des Basiskonstruktors sind die abgeleiteten Elemente also nicht einmal vorhanden. Sie müssen also nicht initialisiert werden und konnten möglicherweise nicht initialisiert werden. Erst wenn der Basiskonstruktor fertig ist, wird das Objekt in ein abgeleitetes Objekt mit vielen nicht initialisierten Feldern geändert, die Sie dann initialisieren.

Wenn Sie in Swift ein abgeleitetes Objekt erstellen, handelt es sich von Anfang an um ein abgeleitetes Objekt. Wenn Methoden überschrieben werden, verwendet die Base init-Methode bereits die überschriebenen Methoden, die möglicherweise auf abgeleitete Elementvariablen zugreifen. Daher sind alle Abgeleitet Membervariablen müssen initialisiert werden , bevor die Basis init - Methode aufgerufen wird.

PS. Sie haben Objective-C erwähnt. In Objective-C wird alles automatisch auf 0 / nil / NO initialisiert. Wenn dieser Wert jedoch nicht der richtige Wert zum Initialisieren einer Variablen ist, kann die Base init-Methode leicht eine Methode aufrufen, die überschrieben wird und die noch nicht initialisierte Variable mit dem Wert 0 anstelle des korrekten Werts verwendet. In Objective-C ist dies kein Verstoß gegen die Sprachregeln (so wird es definiert), sondern offensichtlich ein Fehler in Ihrem Code. In Swift ist dieser Fehler in der Sprache nicht zulässig.

PS. Es gibt einen Kommentar "Ist es von Anfang an ein abgeleitetes Objekt oder kann es aufgrund von Sprachregeln nicht beobachtet werden?" Die abgeleitete Klasse hat ihre eigenen Mitglieder initialisiert, bevor die Base init-Methode aufgerufen wird, und diese abgeleiteten Mitglieder behalten ihre Werte bei. Entweder handelt es sich zum Zeitpunkt des Aufrufs von Base init um ein abgeleitetes Objekt, oder der Compiler müsste etwas ziemlich Seltsames tun. Und direkt nachdem die Base init-Methode alle Base-Instanzmitglieder initialisiert hat, kann sie überschriebene Funktionen aufrufen, was beweist, dass es sich um eine Instanz der abgeleiteten Klasse handelt.

gnasher729
quelle
Sehr sensibel. Ich habe mir die Freiheit genommen, ein Beispiel hinzuzufügen. Fühlen Sie sich frei, einen Rollback durchzuführen, wenn ich unklar war oder den Punkt falsch verstanden habe.
Zomagk
Ist es von Anfang an ein abgeleitetes Objekt oder machen die Sprachregeln dies nicht beobachtbar? Ich denke es ist das letztere.
Deduplikator
9

Das kommt von Swift der Sicherheitsbestimmungen, wie im Abschnitt Zwei-Phasen Initialisierung auf der Sprache doc Initialisierungsseite .

Es stellt sicher, dass jedes Feld vor der Verwendung festgelegt wird (insbesondere Zeiger, um Abstürze zu vermeiden).

Swift erreicht dies mit einer zweiphasigen Initialisierungssequenz: Jeder Initialisierer muss alle seine Instanzfelder initialisieren und dann einen Superklasseninitialisierer aufrufen, um dies ebenfalls zu tun. Erst wenn dies im Baum geschieht, dürfen diese Initialisierer den selfZeiger entkommen lassen und die Instanz aufrufen Methoden oder lesen Sie die Werte der Instanzeigenschaften.

Sie können dann eine weitere Initialisierung durchführen, um sicherzustellen, dass das Objekt gut geformt ist. Insbesondere haben alle nicht optionalen Zeiger gültige Werte. Null ist für sie nicht gültig.

Ziel C ist nicht viel anders, außer dass 0 oder Null immer ein gültiger Wert ist. Daher erfolgt die Initialisierung der ersten Phase durch den Allokator, der nur alle Felder auf 0 setzt. Außerdem verfügt Swift über unveränderliche Felder, sodass diese in Phase 1 initialisiert werden müssen . Und Swift setzt diese Sicherheitsregeln durch.

Jerry101
quelle
Natürlich wäre es mit MI viel schwieriger.
Deduplikator
3

Erwägen

  • Eine in der Basisklasse definierte virtuelle Methode kann in der abgeleiteten Klasse neu definiert werden.
  • Der Auftragnehmer der Basisklasse kann diese virtuelle Methode direkt oder indirekt aufrufen.
  • Die neu definierte virtuelle Methode (in der abgeleiteten Klasse) kann davon abhängen, dass der Wert eines Felds in der abgeleiteten Klasse im Auftragnehmer für abgeleitete Klassen korrekt festgelegt wird.
  • Der Auftragnehmer der abgeleiteten Klasse kann eine Methode in der Basisklasse aufrufen, die davon abhängt, welche Felder im Auftragnehmer der Basisklasse festgelegt wurden.

Daher gibt es kein einfaches Design, das den Auftragnehmer sicher macht, wenn virtuelle Methoden zulässig sind. Swift vermeidet diese Probleme, indem es eine zweiphasige Initialisierung erfordert, wodurch der Programmierer besser geschützt wird und gleichzeitig eine komplexere Sprache entsteht.

Wenn Sie diese Probleme auf nette Weise lösen können, übergeben Sie nicht "Go", sondern fahren Sie direkt mit der Sammlung Ihrer Doktorarbeit fort.

Ian
quelle