Swift init()

In diesem Swift init() Tutorial werden wir ein wichtiges Konzept besprechen, nämlich Swift init oder Swift-Initialisierung. Initialisierung ist das, was passiert, wenn wir eine Instanz eines Typs erstellen.

Initialisierung ist der Prozess der Vorbereitung einer Instanz einer Klasse, Struktur oder Aufzählung zur Verwendung. Dieser Prozess beinhaltet das Festlegen eines Anfangswertes für jede gespeicherte Eigenschaft auf dieser Instanz und das Ausführen aller anderen Einrichtungen oder Initialisierungen, die erforderlich sind, bevor die neue Instanz verwendet werden kann.

Initializer sind ähnlich wie Konstruktoren in der Java-Programmierung. Swift, als eine typsichere Sprache, hat viele Regeln für Initializer festgelegt. Es kann schwierig zu implementieren sein, es sei denn, man hat ein gutes Verständnis des Konzepts.

Swift init() Syntax

init() {
    // initialise the stored properties here.
}

Lassen Sie uns eine Beispielklasse unten ansehen.

class A{
    //Compilation error. No initializer is defined.
    var a : Int
    var b : String
    var c : Int?
    let website = "JournalDev"
}

Die oben genannte Klasse wird nicht kompilieren. Der Swift-Compiler bemängelt, dass die gespeicherten Eigenschaften nicht initialisiert sind. Gespeicherte Eigenschaften können nicht in einem unbestimmten Zustand belassen werden. Dies führt uns zu zwei möglichen Optionen:

  1. Weisen Sie einen Standardwert in der Eigenschaftsdefinition selbst zu.
  2. Verwenden Sie einen Initializer , init(), um die Eigenschaften zu initialisieren.

Lassen Sie uns jede der Ansätze einzeln betrachten.

class A{
    var a : Int = 5
    var b : String = "Hello. How you're doing"
    var c : Int?
    let website = "JournalDev"
}

Hier haben wir einen Standardwert für jede der gespeicherten Eigenschaften festgelegt, daher bietet Swift uns implizit den StandardInitializer an. Alle Eigenschaften und Funktionen können über den Punktoperator an einer Instanz der Klasse aufgerufen werden, sobald sie initialisiert ist.

var object = A()
object.a = 10
object.c = 2

Der zweite Weg ist die Initialisierung der gespeicherten Eigenschaften mit der init()-Methode wie unten gezeigt.

class A{
    var a : Int
    var b : String
    var c : Int?
    let website = "JournalDev"
    
    init(a: Int, b: String) {
        self.a = a
        self.b = b
    }
}

var object = A(a: 5, b: "Hello World")

Anmerkung: Swift Optional ist keine gespeicherte Eigenschaft. Daher müssen sie nicht initialisiert werden. Gespeicherte Eigenschaften werden innerhalb der init()-Methode mit der self-Eigenschaft zugegriffen. Anmerkung: self wird verwendet, um auf die aktuelle Instanz innerhalb ihrer eigenen Instanzmethoden zu verweisen (ähnlich wie this in Java). Der obige Initializer ist der primäre Initializer der Klasse. Er ist auch bekannt als designierter Initializer (wir werden dies später besprechen). Initializer ermöglichen es uns auch, eine konstante Eigenschaft zu modifizieren.

class A{
    
    var a : Int
    var b : String
    var c : Int?
    let website : String
    
    init(a: Int, b: String, website: String) {
        self.a = a
        self.b = b
        self.website = website
    }
}

var object = A(a: 5, b: "Hello World", website: "JournalDev")

Memberwise Initializer für Strukturen

Strukturen als Werttypen benötigen nicht unbedingt einen definierten Initializer . Strukturtypen erhalten automatisch einen memberwise Initializer , es sei denn, Sie haben benutzerdefinierte Initializer definiert. Nachfolgend sind die Code-Schnipsel, die die verschiedenen Möglichkeiten zur Initialisierung einer Struktur beschreiben.


struct Rect{
    var length : Int
    var breadth : Int
}
var r = Rect(length: 5, breadth: 10)


struct Rect{
    var length : Int = 5
    var breadth : Int = 10
}

var r = Rect()
var r1 = Rect(length: 10, breadth: 5)

Da wir den gespeicherten Eigenschaften in dem obigen Abschnitt Standardwerte zugewiesen haben, erhalten wir einen Standard Initializer ohne Memberwise Initialisierung zusammen mit dem Memberwise Initializer .

struct Rect{
    var length : Int
    var breadth : Int
    
    init(length: Int, breadth: Int) {
        self.length =  length + 10
        self.breadth = breadth + 10
    }
}
var r = Rect(length: 10, breadth: 5)

In dem obigen Fall haben wir unsere eigenen benutzerdefinierten Initializer definiert. Verwendung von Parametern ohne externen Namen Wenn ein externer Name für einen Initializer nicht benötigt wird, wird das Unterstrichzeichen ‚_‘ verwendet, um dies anzuzeigen, wie unten gezeigt.


class A{
    
    var a : Int
    var b : String
    var c : Int?
    let website = "JournalDev"
    
    init(_ a: Int, _ b: String) {
        self.a = a
        self.b = b
    }
}

var object = A(5,"Hello World")


struct Rect{
    var length : Int
    var breadth : Int
    
    init(_ length: Int, _ breadth: Int) {
        self.length =  length + 10
        self.breadth = breadth + 10
    }
}
var r = Rect(10, 10)

Arten von Swift-Initializer

Initializer für Klassen können grob in die folgenden Typen klassifiziert werden:

  1. Designierte Initializer : Dies ist der primäre Initializer der Klasse. Er muss alle von seiner Klasse eingeführten Eigenschaften vollständig initialisieren, bevor ein Initializer der Superklasse aufgerufen wird. Eine Klasse kann mehr als einen designierten Initializer haben. Jede Klasse muss mindestens einen designierten Initializer haben.
  2. Convenience-Initializer : Dies sind sekundäre, unterstützende Initializer für eine Klasse. Sie müssen einen designierten Initializer derselben Klasse aufrufen. Diese sind optional und können für eine benutzerdefinierte Einrichtung verwendet werden. Sie werden im gleichen Stil geschrieben, aber mit dem Convenience-Modifikator vor dem init-Schlüsselwort platziert.

class Student{
    var name : String
    var degree : String
    init(name : String, degree: String) {
        self.name = name
        self.degree = degree
    }
    convenience init() {
        self.init(name: "Unnamed", degree: "Computer Science")
    }
}
var student = Student()
student.degree // "Computer Science"
student.name // "Unnamed"

Convenience-Initializer sind nützlich, wenn es darum geht, Standardwerte für gespeicherte Eigenschaften zuzuweisen.

Swift Initializer Delegation für Werttypen

Es ist möglich, einen Initializer aus einem anderen aufzurufen, um Code-Duplikation zu vermeiden. Werttypen wie Strukturen unterstützen keine Vererbung. Daher ist der einzige mögliche Weg, einen Initializer innerhalb derselben Struktur aufzurufen. Ein Beispiel wird unten gegeben.

struct Rect{
    var length : Int
    var breadth : Int
    init(_ length: Int, _ breadth: Int) {
        self.length = length
        self.breadth = breadth
    }
    init(_ length: Int) {
        self.init(length, length)
    }
}
var r = Rect(10, 5)
var r1 = Rect(15) //initialises the length and breadth to 15

Swift Initializer Delegation für Referenztypen

Klassen als Referenztypen unterstützen die Vererbung. Daher können Initializer auch andere Initializer von der Superklasse aufrufen, wodurch Verantwortlichkeiten hinzugefügt werden, um alle Werte richtig zu erben und zu initialisieren. Nachfolgend sind die primären Regeln definiert, um Beziehungen zwischen Initializer n zu handhaben.

  • Ein designierter Initializer muss einen designierten Initializer seiner unmittelbaren Superklasse aufrufen.
  • Ein Convenience-Initializer muss einen anderen Initializer derselben Klasse aufrufen.
  • Ein Convenience-Initializer muss letztendlich einen designierten Initializer aufrufen.

Designierte Initializer müssen immer nach oben delegieren. Convenience-Initializer müssen immer quer delegieren. Das super-Schlüsselwort ist für einen Convenience-Initializer in einer Unterklasse nicht möglich.

Swift Initializer -Erbschaft und Überschreiben

Unterklassen in Swift erben standardmäßig nicht die Initializer ihrer Superklasse, es sei denn, bestimmte Bedingungen sind erfüllt (Automatische Initializer -Erbschaft). Dies wird getan, um eine halbfertige Initialisierung in der Unterklasse zu verhindern. Lassen Sie uns ansehen, wie designierte und Convenience-Initializer ihren Weg durch die Vererbung finden. Wir werden eine Fahrzeug-Basisklasse definieren, die von den relevanten Unterklassen geerbt wird. Wir werden Enums als Typ in den Klassen verwenden. Unsere Basisklasse Fahrzeug ist wie unten gezeigt definiert.

enum VehicleType : String {
    case twoWheeler = "TwoWheeler"
    case fourWheeler = "FourWheeler"
}
class Vehicle{
    var vehicleType : VehicleType
    init(vehicleType: VehicleType) {
        self.vehicleType = vehicleType
        print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
    }
    convenience init() {
        self.init(vehicleType: .fourWheeler)
    }
}
var v = Vehicle(vehicleType: .twoWheeler)

Anmerkung: Der Convenience-Initializer muss den bestimmten Initializer derselben Klasse mit self.init aufrufen. Lassen Sie uns eine Unterklasse der oben genannten Klasse wie unten gezeigt definieren.

enum TwoWheelerType : String {
    case scooty = "Scooty"
    case bike = "Bike"
}

class TwoWheeler : Vehicle{
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
    }
}

Wichtige Punkte, die zu beachten sind:

  • Der bestimmte Initializer der Unterklasse muss seine eigenen Eigenschaften initialisieren, bevor er den bestimmten Initializer der Superklasse aufruft.
  • Eine Unterklasse kann vererbte Eigenschaften der Superklasse erst nach dem Aufruf von super.init ändern.

Der folgende Code würde zu einem Kompilierungsfehler führen.

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        self.vehicleType = vType //Won't compile
        super.init(vehicleType: vType)
        //self.vehicleType = .fourWheeler //This would work.
        
    }
}

var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)

Wie zuvor erklärt, wird der Initializer der Superklasse nicht automatisch in der Unterklasse vererbt. Daher würde die folgende Initialisierung fehlschlagen.

var t = TwoWheeler(vehicleType: .twoWheeler) // manufacturer property isn't initialized.

Um einen Initializer zu überschreiben, muss der Initializer der Unterklasse mit dem bestimmten Initializer der Superklasse übereinstimmen. In diesem Fall wird das Schlüsselwort override dem Initializer hinzugefügt.

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
    
    override init(vehicleType: VehicleType)
    {
        print("Class TwoWheeler. Overriden Initializer. \(vehicleType.rawValue)")
        self.twoWheelerType = .bike
        self.manufacturer = "Not defined"
        super.init(vehicleType: vehicleType)
    }

Der untenstehende Initializer überschreibt nicht den aus der Superklasse, da der Parametername unterschiedlich ist.

//This would give a compile-time error since the parameter v doesn't match with the superclass.
override init(v: VehicleType)
    {
        self.twoWheelerType = .bike
        self.manufacturer = "Not defined"
        super.init(vehicleType: v)
    }

Verwendung des Convenience-Initializers, um den Initializer der Superklasse zu überschreiben.

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
    
    override convenience init(vehicleType: VehicleType) {
        self.init(twoWheelerType: .bike, manufacturer: "Not Defined", vType: .twoWheeler)
        self.vehicleType = vehicleType
    }
}
var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)
t = TwoWheeler(vehicleType: .twoWheeler)

//Output
Following gets printed on the console:
Class TwoWheeler. Scooty manufacturer is Hero Honda
Class Vehicle. vehicleType is TwoWheeler

Class TwoWheeler. Bike manufacturer is Not Defined
Class Vehicle. vehicleType is TwoWheeler

Der Komfort-Initializer hat das Schlüsselwort override angehängt. Er ruft den vorgesehenen Initializer der gleichen Klasse auf. Hinweis: Die Reihenfolge der Schlüsselwörter convenience und override spielt keine Rolle.

Erforderliche Initialisierungsprogramme

Die Angabe des Schlüsselworts required vor dem Initializer bedeutet, dass jede Unterklasse diesen Initializer implementieren muss. Außerdem muss der erforderliche Modifikator auch in den jeweiligen Unterklassenimplementierungen vorhanden sein. Nachfolgend ein Beispiel für Required Initializers in den beiden oben genannten Klassen.

class Vehicle{
    
    var vehicleType : VehicleType
    
    required init(vehicleType: VehicleType) {
        self.vehicleType = vehicleType
        print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
    }
    
    convenience init()
    {
        self.init(vehicleType: .fourWheeler)
    }
}

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
    
     required init(vehicleType: VehicleType) {
        self.manufacturer = "Not Defined"
        self.twoWheelerType = .bike
        super.init(vehicleType: vehicleType)
    }
}

Hinweis: Das Hinzufügen eines erforderlichen Modifikators zeigt an, dass der Initializer überschrieben werden würde. Daher kann das Schlüsselwort override im obigen Fall weggelassen werden. Verwendung eines Required-Initializers mit Convenience Required- und Convenience-Initializer sind unabhängig voneinander und können zusammen verwendet werden. Erstellen wir eine weitere Unterklasse von Vehicle, um die gemeinsame Verwendung von Required- und Convenience-Modifikatoren zu demonstrieren.

enum FourWheelerType : String {
    case car = "Car"
    case bus = "Bus"
    case truck = "Truck"
}

class FourWheeler : Vehicle {
    var fourWheelerType : FourWheelerType
    var name : String

    init(fourWheelerType : FourWheelerType, name: String, vehicleType: VehicleType) {
        self.fourWheelerType = fourWheelerType
        self.name = name
        print("Class FourWheeler. \(self.fourWheelerType.rawValue) Model is \(self.name)")
        super.init(vehicleType: vehicleType)
        self.vehicleType = vehicleType
    }

    required convenience init(vehicleType: VehicleType) {
        self.init(fourWheelerType: .bus, name: "Mercedes", vehicleType: vehicleType)
    }
}

class Car : FourWheeler {
    var model : String

    init(model: String) {
        self.model = model
        print("Class Car. Model is \(self.model)")
        super.init(fourWheelerType: .car, name: self.model, vehicleType: .fourWheeler)
    }

    required init(vehicleType: VehicleType) {
        self.model = "Not defined"
        print("Class Car. Model is \(self.model)")
        super.init(fourWheelerType: .car, name: self.model, vehicleType: vehicleType)
    }
}

Wichtige Punkte zum Beachten im obigen Code-Schnipsel:

  • Convenience-Initializer sind sekundäre Initializer in einer Klasse.
  • Das Setzen eines Convenience-Initializers als erforderlich bedeutet, dass seine Implementierung in der Unterklasse verpflichtend ist.

Automatische Initializer -Erbschaft

Es gibt zwei Umstände, unter denen eine Unterklasse automatisch die Initializer von der Superklasse erbt.

  • Definieren Sie keine designierten Initializer in Ihrer Unterklasse.
  • Implementieren Sie alle designierten Initializer der Superklasse. Alle Convenience-Initializer würden auch automatisch geerbt.

Die erste Regel in Aktion wird im folgenden Ausschnitt gezeigt:

class Name {
    var name: String

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

class Tutorial: Name {
    var tutorial : String? = "Swift Initialization"
}

var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "JournalDev")

Die zweite Regel in Aktion wird im folgenden Ausschnitt gezeigt.

class Name {
    
    var name: String
    
    init(n: String) {
        self.name = n
    }
    
    convenience init()
    {
        self.init(n: "No name assigned")
    }
}

class Tutorial: Name {
    
    var tutorial : String? = "Swift Tutorial"
    
    override init(n : String) {
        super.init(n: n)
    }
}

var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "JournalDev")
var childObject2 = Tutorial()
print(childObject2.name) //prints "No name assigned

Die Convenience-Initializer der Superklasse sind automatisch in der Unterklasse im obigen Code verfügbar.

Swift Fehlschlagender Initializer

Wir können einen fehlgeschlagenen Initializer mit dem Schlüsselwort init? in Klassen, Strukturen oder Aufzählungen definieren, der ausgelöst wird, wenn der Initialisierungsprozess fehlschlägt. Die Initialisierung kann aus verschiedenen Gründen fehlschlagen: Ungültige Parameterwerte, Fehlen einer externen Quelle usw. Ein fehlgeschlagener Initializer erstellt einen optionalen Wert des Typs, den er initialisiert. Wir werden nil zurückgeben, um einen Initialisierungsfehler auszulösen (obwohl ein init nichts zurückgibt).

Fehlgeschlagene Initializer mit Strukturen

struct SName {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}

var name = SName(name: "JournalDev")
if name != nil {
    print("init success") //this gets displayed
}
else{
    print("init failed")
}
name  = SName(name: "")

if name != nil {
    print("init success")
}
else{
    print("init failed") //this gets displayed
}

Fehlgeschlagene Initializer mit Aufzählungen

enum CharacterExists {
    case A, B
    init?(symbol: Character) {
        switch symbol {
        case "A":
            self = .A
        case "B":
            self = .B
        default:
            return nil
        }
    }
}


let ch = CharacterExists(symbol: "C")
if ch != nil {
    print("Init failed. Character doesn't exist")
}


class CName {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}
var name  = CName(name: "")

if name != nil {
    print("init success")
}
else{
    print("init failed")
}

Anmerkung: Ein fehlgeschlagener Initializer und ein nicht fehlgeschlagener Initializer können nicht die gleichen Parametertypen und -namen haben.

Überschreiben eines fehlgeschlagenen Initializer s

Sie können einen fehlgeschlagenen Initializer in Ihrer Unterklasse überschreiben. Ein fehlgeschlagener Initializer kann mit einem nicht fehlgeschlagenen Initializer überschrieben werden, aber umgekehrt ist dies nicht möglich. Ein Beispiel für das Überschreiben eines fehlgeschlagenen mit einem nicht fehlgeschlagenen Initializer wird unten gegeben.

class CName {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}
var name = CName(name: "")

class SubName : CName {
    var age: Int
    override init(name: String) {
        self.age = 23
        super.init(name: name)!  
    }
}

Anmerkung: Zum Aufrufen eines fehlgeschlagenen Initializer s aus der Superklasse wird im Rahmen der Implementierung des nicht fehlgeschlagenen Initializer s der Unterklasse das erzwungene Entpacken verwendet. Damit endet das Swift-Init-Tutorial.

Kostenlosen Account erstellen

Registrieren Sie sich jetzt und erhalten Sie Zugang zu unseren Cloud Produkten.

Das könnte Sie auch interessieren: