Was ist der Unterschied zwischen Convenience Init und Init in schnellen, expliziten Beispielen besser?

Antworten:

102

Standard init:

Bestimmte Initialisierer sind die primären Initialisierer für eine Klasse. Ein benannter Initialisierer initialisiert alle von dieser Klasse eingeführten Eigenschaften vollständig und ruft einen geeigneten Superklassen-Initialisierer auf, um den Initialisierungsprozess in der Superklassenkette fortzusetzen.

convenience init::

Convenience-Initialisierer sind sekundär und unterstützen Initialisierer für eine Klasse. Sie können einen Convenience-Initialisierer definieren, um einen bestimmten Initialisierer aus derselben Klasse wie den Convenience-Initialisierer aufzurufen, wobei einige der Parameter des festgelegten Initialisierers auf Standardwerte gesetzt sind. Sie können auch einen praktischen Initialisierer definieren, um eine Instanz dieser Klasse für einen bestimmten Anwendungsfall oder Eingabewerttyp zu erstellen.

gemäß der Swift-Dokumentation

Kurz gesagt bedeutet dies, dass Sie einen praktischen Initialisierer verwenden können, um das Aufrufen eines bestimmten Initialisierers schneller und "bequemer" zu machen. Convenience-Initialisierer erfordern daher die Verwendung von self.initanstelle des, den super.initSie möglicherweise bei einer Überschreibung eines bestimmten Initialisierers sehen.

Pseudocode-Beispiel:

init(param1, param2, param3, ... , paramN) {
     // code
}

// can call this initializer and only enter one parameter,
// set the rest as defaults
convenience init(myParamN) {
     self.init(defaultParam1, defaultParam2, defaultParam3, ... , myParamN)
}

Ich benutze diese häufig beim Erstellen von benutzerdefinierten Ansichten und solchen, die lange Initialisierer mit hauptsächlich Standardeinstellungen haben. Die Dokumente erklären besser als ich, sehen Sie sie sich an!

treyhakanson
quelle
72

Convenience-Initialisierer werden verwendet, wenn Sie eine Klasse mit vielen Eigenschaften haben, die es irgendwie "schmerzhaft" macht, den Witz immer mit all diesen Variablen zu initialisieren. Was Sie also mit dem Convenience-Initialisierer tun, ist, dass Sie nur einige der Variablen übergeben, um die zu initialisieren Objekt, und weisen Sie den Rest mit einem Standardwert zu. Es gibt ein sehr gutes Video auf der Ray Wenderlich-Website, nicht sicher, ob es kostenlos ist oder nicht, weil ich ein bezahltes Konto habe. Hier ist ein Beispiel, in dem Sie sehen können, dass ich mein Objekt nicht mit all diesen Variablen initialisiere, sondern nur einen Titel gebe.

struct Scene {
  var minutes = 0
}

class Movie {
  var title: String
  var author: String
  var date: Int
  var scenes: [Scene]

  init(title: String, author: String, date: Int) {
    self.title = title
    self.author = author
    self.date = date
    scenes = [Scene]()
  }

  convenience init(title:String) {
    self.init(title:title, author: "Unknown", date:2016)
  }

  func addPage(page: Scene) {
    scenes.append(page)
  }
}


var myMovie = Movie(title: "my title") // Using convenicence initializer
var otherMovie = Movie(title: "My Title", author: "My Author", date: 12) // Using a long normal initializer
Mago Nicolas Palacios
quelle
4
Gutes Beispiel. Dies machte das Konzept klar. Vielen Dank.
Arjun Kalidas
5
Wir können auch init () mit Standardwerten für Autor und Datumsparameter als `init (Titel: String, Autor: String =" Unbekannt ", Datum: Int = 2016) {self.title = title self.author = author self. Datum = Datum Szenen = [Szene] ()} `Warum brauchen wir dann einen praktischen Initialisierer?
Shripad20
@ shripad20 gleiche Frage hier. Haben Sie herausgefunden, warum wir Convenience verwenden müssen, obwohl wir einen anderen Initialisierer erstellen und Standardparameter daraus senden können? Bitte lassen Sie mich wissen, wenn Sie eine Antwort finden
user100
Die Bequemlichkeit ist zusätzlich zu „main“ self.init während self.init mit Standardparametern ist Ersatz für den „main“ self.init. hackingwithswift.com/example-code/language/…
user1105951
@ shripad20 Schauen Sie sich mein Beispiel unten an, um zu verdeutlichen, wo Convenience-Initialisierer die Standardwerte übertreffen. Ich hoffe es hilft.
Chris Graf
14

Hier ist ein einfaches Beispiel aus dem Apple Developer Portal .

Grundsätzlich ist der festgelegte Initialisierer der init(name: String), der sicherstellt, dass alle gespeicherten Eigenschaften initialisiert werden.

Der init()Convenience-Initialisierer setzt ohne Argument automatisch den Wert der namegespeicherten Eigenschaft [Unnamed]mithilfe des angegebenen Initialisierers auf.

class Food {
    let name: String

    // MARK: - designated initializer
    init(name: String) {
        self.name = name
    }

    // MARK: - convenience initializer
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}

// MARK: - Examples
let food = Food(name: "Cheese") // name will be "Cheese"
let food = Food()               // name will be "[Unnamed]"

Dies ist nützlich, wenn Sie mit großen Klassen mit mindestens einigen gespeicherten Eigenschaften arbeiten. Ich würde empfehlen, mehr über Optionen und Vererbung im Apple Developer Portal zu lesen .

Dirtydanee
quelle
6

Wo Convenience-Initialisierer die Einstellung der Standardparameterwerte übertreffen

Für mich convenience initializerssind sie nützlich, wenn mehr zu tun ist, als einfach einen Standardwert für eine Klasseneigenschaft festzulegen.

Klassenimplementierung mit bezeichnetem init ()

Andernfalls würde ich einfach den Standardwert in der initDefinition festlegen , z.

class Animal {

    var race: String // enum might be better but I am using string for simplicity
    var name: String
    var legCount: Int

    init(race: String = "Dog", name: String, legCount: Int = 4) {
        self.race = race
        self.name = name
        self.legCount = legCount // will be 4 by default
    }
}

Klassenerweiterung mit Komfort init ()

Es kann jedoch mehr zu tun geben, als nur einen Standardwert festzulegen, und das ist convenience initializersnützlich:

extension Animal {
    convenience init(race: String, name: String) {
        var legs: Int

        if race == "Dog" {
            legs = 4
        } else if race == "Spider" {
            legs = 8
        } else {
            fatalError("Race \(race) needs to be implemented!!")
        }

        // will initialize legCount automatically with correct number of legs if race is implemented
        self.init(race: race, name: name, legCount: legs)
    }
}

Anwendungsbeispiele

// default init with all default values used
let myFirstDog = Animal(name: "Bello")

// convenience init for Spider as race
let mySpider = Animal(race: "Spider", name: "Itzy")

// default init with all parameters set by user
let myOctopus = Animal(race: "Octopus", name: "Octocat", legCount: 16)

// convenience init with Fatal error: Race AlienSpecies needs to be implemented!!
let myFault = Animal(race: "AlienSpecies", name: "HelloEarth")
Chris Graf
quelle
1
gut und einfach hier erklärt
vivi
4

Ein Convenience-Initialisierer kann in einer Klassenerweiterung definiert werden . Aber ein Standard - kann nicht.

Serg
quelle
Obwohl dies überhaupt keine vollständige Antwort auf die Frage ist, habe ich dies noch nicht in Betracht gezogen, danke!
Marc-André Weibezahn
2

Hinweis: Lesen Sie den gesamten Text

Bestimmte Initialisierer sind die primären Initialisierer für eine Klasse. Ein designierter Initialisierer initialisiert alle von dieser Klasse eingeführten Eigenschaften vollständig und ruft einen geeigneten Superklassen-Initialisierer auf, um den Initialisierungsprozess bis zur Superklassenkette fortzusetzen.

Convenience-Initialisierer sind sekundär und unterstützen Initialisierer für eine Klasse. Sie können einen Convenience-Initialisierer definieren, um einen bestimmten Initialisierer aus derselben Klasse wie den Convenience-Initialisierer aufzurufen, wobei einige der Parameter des festgelegten Initialisierers auf Standard gesetzt sind.

Bestimmte Initialisierer für Klassen werden wie einfache Initialisierer für Werttypen geschrieben:

init(parameters) {
statements
}

Convenience-Initialisierer werden im gleichen Stil geschrieben, jedoch mit dem Convenience-Modifikator vor dem Schlüsselwort init, getrennt durch ein Leerzeichen:

convenience init(parameters) {
statements
}

Ein praktisches Beispiel ist wie folgt:

class Food {
var name: String
init(name: String) {
    self.name = name
}
convenience init() {
    self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon”

Der Initialisierer init (name: String) aus der Food-Klasse wird als designierter Initialisierer bereitgestellt, da er sicherstellt, dass alle gespeicherten Eigenschaften einer neuen Food-Instanz vollständig initialisiert werden. Die Food-Klasse hat keine Superklasse, daher muss der Initialisierer init (name: String) nicht super.init () aufrufen, um die Initialisierung abzuschließen.

„Die Food-Klasse bietet auch den Convenience-Initialisierer init () ohne Argumente. Der Initialisierer init () stellt einen Standardplatzhalternamen für ein neues Lebensmittel bereit, indem er an den Init (Name: String) der Lebensmittelklasse mit dem Namenswert [Unbenannt] delegiert: ”

let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]”

Die zweite Klasse in der Hierarchie ist eine Unterklasse von Lebensmitteln namens RecipeIngredient. Die RecipeIngredient-Klasse modelliert eine Zutat in einem Kochrezept. Es führt eine Int-Eigenschaft namens Quantity ein (zusätzlich zu der Name-Eigenschaft, die es von Food erbt) und definiert zwei Initialisierer zum Erstellen von RecipeIngredient-Instanzen:

class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
    self.quantity = quantity
    super.init(name: name)
}
override convenience init(name: String) {
    self.init(name: name, quantity: 1)
}
}

Die RecipeIngredient-Klasse verfügt über einen einzelnen festgelegten Initialisierer, init (Name: String, Menge: Int), mit dem alle Eigenschaften einer neuen RecipeIngredient-Instanz aufgefüllt werden können. Dieser Initialisierer weist zunächst der übergebenen Mengeneigenschaft das übergebene Mengenargument zu. Dies ist die einzige neue Eigenschaft, die von RecipeIngredient eingeführt wurde. Anschließend delegiert der Initialisierer an den Initialisierer init (Name: String) der Food-Klasse.

Seite: 536 Auszug aus: Apple Inc. „Die Swift-Programmiersprache (Swift 4).“ iBooks. https://itunes.apple.com/pk/book/the-swift-programming-language-swift-4-0-3/id881256329?mt=11

Abuzar Manzoor
quelle
2

Dies ist praktisch, wenn Sie nicht jede Eigenschaft für eine Klasse angeben müssen. Wenn ich zum Beispiel alle Abenteuer mit einem HP-Startwert von 100 erstellen möchte, würde ich diesen folgenden Convenience-Init verwenden und einfach einen Namen hinzufügen. Dies wird den Code erheblich reduzieren.

class Adventure { 

// Instance Properties

    var name: String
    var hp: Int
    let maxHealth: Int = 100

    // Optionals

    var specialMove: String?

    init(name: String, hp: Int) {

        self.name = name
        self.hp = hp
    }

    convenience init(name: String){
        self.init(name: name, hp: 100)
    }
}
Nirav Jain
quelle
2

Das convenience initmacht es optional, eine Klasse mit Werten zu initialisieren.

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Badr
quelle
1

Alle Antworten klingen gut, aber lassen Sie es uns anhand eines einfachen Beispiels verstehen

class X{                                     
   var temp1
   init(a: Int){
        self.temp1 = a
       }

Jetzt wissen wir, dass eine Klasse eine andere Klasse erben kann

class Z: X{                                     
   var temp2
   init(a: Int, b: Int){
        self.temp2 = b
        super.init(a: a)
       }

In diesem Fall müssen Sie beim Erstellen einer Instanz für die Klasse Z beide Werte 'a' und 'b' angeben.

let z = Z(a: 1, b: 2)

Was ist jedoch, wenn Sie nur den Wert von b übergeben möchten und Rest den Standardwert für andere übernehmen soll? In diesem Fall müssen Sie andere Werte als Standardwert initialisieren. Aber warte wie?, Dafür musst du es vorher nur in der Klasse gut einstellen.

//This is inside the class Z, so consider it inside class Z's declaration
convenience init(b: Int){
    self.init(a: 0, b: b)
}
convenience init(){
    self.init(a: 0, b: 0)
}

Und jetzt können Sie Instanzen der Klasse Z erstellen, indem Sie einige, alle oder keine Werte für die Variablen angeben.

let z1 = Z(b: 2)
let z2 = Z()
Shubham Mishra
quelle
1

Es ist sinnvoll, wenn Ihr Anwendungsfall darin besteht, einen Initialisierer in einem anderen Initialisierer derselben Klasse aufzurufen .

Versuchen Sie dies auf dem Spielplatz zu tun

class Player {
    let name: String
    let level: Int

    init(name: String, level: Int) {
        self.name = name
        self.level = level
    }
    
    init(name: String) {
        self.init(name: name, level: 0) //<- Call the initializer above?

        //Sorry you can't do that. How about adding a convenience keyword?
    }
}

Player(name:"LoseALot")

Mit Convenience-Keyword

class Player {
    let name: String
    let level: Int

    init(name: String, level: Int) {
        self.name = name
        self.level = level
    }
    
    //Add the convenience keyword
    convenience init(name: String) {
        self.init(name: name, level: 0) //Yes! I am now allowed to call my fellow initializer!
    }
}
jeromegamo
quelle