[properties] Fehler in der Swift-Klasse: Die Eigenschaft wurde beim Aufruf von super.init nicht initialisiert



Answers

Swift hat eine sehr klare, spezifische Abfolge von Operationen, die in Initialisierern durchgeführt werden. Fangen wir mit einigen grundlegenden Beispielen an und arbeiten wir uns zu einem allgemeinen Fall hoch.

Nehmen wir ein Objekt A. Wir definieren es wie folgt.

class A {
    var x: Int
    init(x: Int) {
        self.x = x
    }
}

Beachten Sie, dass A keine Oberklasse hat und daher keine super.init () - Funktion aufrufen kann, da sie nicht existiert.

OK, also lassen Sie uns nun A mit einer neuen Klasse namens B. ableiten.

class B: A {
    var y: Int
    init(x: Int, y: Int) {
        self.y = y
        super.init(x: x)
    }
}

Dies ist eine Abkehr von Objective-C, wobei [super init] typischerweise zuerst vor allem anderen genannt wird. Nicht so in Swift. Sie sind dafür verantwortlich, dass Ihre Instanzvariablen in einem konsistenten Zustand sind, bevor Sie irgendetwas anderes tun, einschließlich Aufrufmethoden (einschließlich des Initialisierers Ihrer Oberklasse).

Question

Ich habe zwei Klassen, Shape und Square

class Shape {
    var numberOfSides = 0
    var name: String
    init(name:String) {
        self.name = name
    }
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

class Square: Shape {
    var sideLength: Double

    init(sideLength:Double, name:String) {
        super.init(name:name) // Error here
        self.sideLength = sideLength
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

Bei der obigen Implementierung bekomme ich den Fehler:

property 'self.sideLength' not initialized at super.init call
    super.init(name:name)

Warum muss ich self.sideLength vor dem Aufruf von super.init ?




Aus den docs

Sicherheitsüberprüfung 1

Ein designierter Initialisierer muss sicherstellen, dass alle von seiner Klasse eingeführten Eigenschaften initialisiert werden, bevor er an einen Superklasseninitialisierer delegiert wird.

Warum brauchen wir einen Sicherheitscheck?

Um dies zu beantworten, muss der Initialisierungsprozess in swift durchlaufen werden.

Zwei-Phasen-Initialisierung

Die Klasseninitialisierung in Swift ist ein zweiphasiger Prozess. In der ersten Phase wird jeder gespeicherten Eigenschaft ein Initialwert von der Klasse zugewiesen, die sie eingeführt hat. Sobald der Anfangszustand für jede gespeicherte Eigenschaft bestimmt worden ist, beginnt die zweite Phase, und jeder Klasse wird die Möglichkeit gegeben, ihre gespeicherten Eigenschaften weiter anzupassen, bevor die neue Instanz als bereit zur Verwendung betrachtet wird.

Die Verwendung eines zweiphasigen Initialisierungsprozesses macht die Initialisierung sicher, während jeder Klasse in einer Klassenhierarchie immer noch volle Flexibilität gegeben wird. Die zweiphasige Initialisierung verhindert, dass auf Eigenschaftswerte zugegriffen wird, bevor sie initialisiert werden , und verhindert, dass Eigenschaftswerte unerwartet von einem anderen Initialisierer auf einen anderen Wert gesetzt werden.

Um sicherzustellen, dass der zweistufige Initialisierungsprozess wie oben definiert durchgeführt wird, gibt es vier Sicherheitschecks, von denen einer ist:

Sicherheitsüberprüfung 1

Ein designierter Initialisierer muss sicherstellen, dass alle von seiner Klasse eingeführten Eigenschaften initialisiert werden, bevor er an einen Superklasseninitialisierer delegiert wird.

Jetzt spricht die Zwei-Phasen-Initialisierung niemals über die Reihenfolge, aber diese Sicherheitsüberprüfung führt super.init , um nach der Initialisierung aller Eigenschaften super.init zu werden.

Sicherheitsüberprüfung 1 könnte irrelevant erscheinen, da eine zweiphasige Initialisierung verhindert, dass auf Eigenschaftswerte zugegriffen wird, bevor sie initialisiert werden, ohne diese Sicherheitsüberprüfung 1.

Wie in diesem Beispiel

class Shape {
    var name: String
    var sides : Int
    init(sides:Int, named: String) {
        self.sides = sides
        self.name = named
    }
}

class Triangle: Shape {
    var hypotenuse: Int
    init(hypotenuse:Int) {
        super.init(sides: 3, named: "Triangle") 
        self.hypotenuse = hypotenuse
    }
}

Triangle.init hat jede Eigenschaft vor der Verwendung initialisiert. Daher scheint Sicherheitscheck 1 irrelevant zu sein,

Aber dann könnte es ein anderes Szenario geben, ein bisschen komplex,

class Shape {
    var name: String
    var sides : Int
    init(sides:Int, named: String) {
        self.sides = sides
        self.name = named
        printShapeDescription()
    }
    func printShapeDescription() {
        print("Shape Name :\(self.name)")
        print("Sides :\(self.sides)")
    }
}

class Triangle: Shape {
    var hypotenuse: Int
    init(hypotenuse:Int) {
        self.hypotenuse = hypotenuse
        super.init(sides: 3, named: "Triangle")
    }

    override func printShapeDescription() {
        super.printShapeDescription()
        print("Hypotenuse :\(self.hypotenuse)")
    }
}

let triangle = Triangle(hypotenuse: 12)

Ausgabe :

Shape Name :Triangle
Sides :3
Hypotenuse :12

Wenn wir die super.init vor dem Setzen der hypotenuse aufgerufen hätten, hätte der Aufruf von super.init dann printShapeDescription() aufgerufen, und da dieser überschrieben wurde, würde er zuerst auf die triangle-Klassenimplementierung von printShapeDescription() . Die printShapeDescription() der Triangle-Klasse printShapeDescription() auf die hypotenuse eine nicht optionale Eigenschaft, die noch nicht initialisiert wurde. Dies ist nicht zulässig, da die zweiphasige Initialisierung verhindert, dass auf Eigenschaftswerte zugegriffen wird, bevor sie initialisiert werden

super.init also sicher, dass die Zwei-Phasen-Initialisierung wie definiert durchgeführt wird. Es muss eine spezifische Reihenfolge für den Aufruf von super.init , super.init nach der Initialisierung aller Eigenschaften, die von der self eingeführt wurden




swift erzwingt die Initialisierung jedes Members, bevor es jemals benutzt wird. Da es nicht sicher sein kann, was passiert, wenn es sich um Superswitch handelt, ist es fehlerfrei: besser sicher als Nachsicht




Swift erlaubt Ihnen nicht, die Superklasse zu initialisieren, ohne die Eigenschaften zu initialisieren, die Umkehrung von Obj C. Sie müssen also alle Eigenschaften initialisieren, bevor Sie "super.init" aufrufen.

Bitte gehen Sie zu http://blog.scottlogic.com/2014/11/20/swift-initialisation.html . Es gibt eine schöne Erklärung für Ihr Problem.




Du legst nur in der falschen Reihenfolge an.

     class Shape2 {
        var numberOfSides = 0
        var name: String
        init(name:String) {
            self.name = name
        }
        func simpleDescription() -> String {
            return "A shape with \(numberOfSides) sides."
        }
    }

    class Square2: Shape2 {
        var sideLength: Double

        init(sideLength:Double, name:String) {

            self.sideLength = sideLength
            super.init(name:name) // It should be behind "self.sideLength = sideLength"
            numberOfSides = 4
        }
        func area () -> Double {
            return sideLength * sideLength
        }
    }



Related