web-dev-qa-db-de.com

Warum benötigte Initialisierer in Swift-Klassen verwenden?

Ich versuche zu verstehen, wie das Schlüsselwort required in Swift-Klassen verwendet wird.

class SomeClass 
{
    required init() {
        // initializer implementation goes here
    }
}

required zwingt mich nicht, die Methode in meiner Kinderklasse zu implementieren. Wenn ich den mit required bezeichneten Initialisierer meiner übergeordneten Klasse überschreiben möchte, muss ich required und nicht override schreiben. Ich weiß, wie es funktioniert, kann aber nicht verstehen, warum ich das tun soll.

Was ist der Vorteil von required? Soweit ich das beurteilen kann, haben Sprachen wie C # so etwas nicht und funktionieren gut mit override.

43
TalkingCode

Es ist eigentlich nur eine Möglichkeit, den Compiler zu befriedigen, um sicherzustellen, dass diese Klasse, falls sie Unterklassen hätte, denselben Initialisierer erben oder implementieren würde. In diesem Punkt besteht Zweifel, dass wenn eine Unterklasse über einen eigenen Initialisierer verfügt, keine Initialisierer der Oberklasse vererbt werden . So ist es möglich, dass eine Oberklasse einen Initialisierer und die Unterklasse nicht hat. required überwindet diese Möglichkeit.

Eine Situation, in der der Compiler auf diese Weise befriedigt werden muss, umfasst Protokolle und funktioniert folgendermaßen:

protocol Flier {
    init()
}
class Bird: Flier {
    init() {} // compile error
}

Das Problem ist, dass wenn Bird eine Unterklasse hätte, diese Unterklasse init implementieren oder erben müsste, und Sie haben dies nicht garantiert. Die init von Bird als required zu kennzeichnen, garantiert dies.

Alternativ können Sie Bird als final markieren, um so das Gegenteil zu gewährleisten, nämlich dass es niemals eine Unterklasse geben wird.

In einer anderen Situation haben Sie eine Factory-Methode, die eine Klasse oder ihre Unterklasse durch Aufrufen desselben Initialisierers erstellen kann:

class Dog {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class NoisyDog: Dog {

}

func dogMakerAndNamer(whattype: Dog.Type) -> Dog {
    let d = whattype.init(name: "Fido") // compile error
    return d
}

dogMakerAndNamer ruft den init(name:)-Initialisierer für Dog oder eine Dog-Unterklasse auf. Aber wie kann der Compiler sicher sein, dass eine Unterklasse einen init(name:)-Initialisierer hat? Die required-Bezeichnung beruhigt die Ängste des Compilers.

73
matt

Ich möchte auf eine andere Lösung aufmerksam machen, die von Required bereitgestellt wurde, abgesehen von Matt oben. 

class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"

}
let instanceSubClass = subClass()
instanceSubClass.name        //output: "Untitled"
instanceSubClass.neakName    //output: "Subclass Untitled"

Wie Sie in obigem Beispiel nachprüfen können, habe ich required init() für superClass erklärt, der init()-Initialisierer von superClass hat standardmäßig subClass geerbt. Sie können also eine Instanz der Unterklasse let instanceSubClass = subClass() erstellen. 

Angenommen, Sie möchten einen bestimmten Initializer in subClass hinzufügen, um der gespeicherten Eigenschaft neakName den Laufzeitwert zuzuweisen. Natürlich können Sie es hinzufügen, aber dies führt dazu, dass keine Initialisierer von der Superklasse an subClass vererbt werden. Wenn Sie also eine Instanz von subClass erstellen, werden Sie sie wie unten beschrieben über einen eigenen festgelegten Initialisierer erstellen. 

class superClass{
    var name: String
    init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"

Hier oben können Sie keine Instanz von subClass erstellen, indem Sie einfach subClass(), erstellen. Wenn Sie jedoch möchten, dass jede Unterklasse von superClass über einen eigenen init()-Initialisierer verfügt, um eine direkte Instanz von subClass() zu erstellen. Platzieren Sie einfach required vor init() in superClass. Dadurch werden Sie dazu gezwungen, init()-Initialisierer auch für subClass hinzuzufügen - wie unten. 

class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}    // Compiler error <------------ required `init()` must be provided by subClass.
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"  

SO, verwenden Sie das required-Schlüsselwort vor der Initialisierung für die Superklasse, wenn Sie möchten, dass alle Unterklassen required initializer der Superklasse implementiert wurden.

6
Kiran Jasvanee

Nach der Dokumentation :

Write the required modifier before the definition of a class initializer to
indicate that every subclass of the class must implement that initializer

Ja, also erzwingen, dass alle untergeordneten Klassen diesen Konstruktor implementieren müssen. Dies ist jedoch nicht erforderlich

 if you can satisfy the requirement with an inherited initializer.

Wenn Sie also komplexere Klassen erstellt haben, die nicht vollständig mit einem übergeordneten Konstruktor initialisiert werden können, müssen Sie den erforderlichen Konstruktor implementieren.

Beispiel aus der Dokumentation (mit ein paar zusätzlichen Sachen):

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

class SomeSubclass: SomeClass {
    let thisNeedsToBeInitialized: String
    required init() {
        // subclass implementation of the required initializer goes here
        self.thisNeedsToBeInitialized = "default value"
    }
}
3
pseudonym117

Wenn Sie versuchen, einen eigenen Initialisierer in der Unterklasse hinzuzufügen, müssen Sie bestimmten Elementen folgen, die in der Superklasse deklariert wurden. So stellen Sie sicher, dass Sie die erforderliche Methode nicht vergessen. Wenn Sie vergessen, gibt der Compiler die Fehlermeldung // fatal error, we've not included the required init() . Ein weiterer Grund ist, dass eine Reihe von Bedingungen erstellt wird, die jeder Unterklasse folgen sollten, wenn die Unterklasse ihren eigenen Initialisierer definiert.

0
Amit89