Jaka jest różnica między wygodą init a init w szybkich, wyraźnych przykładach lepiej

Odpowiedzi:

105

Standardowe init:

Wyznaczone inicjatory są podstawowymi inicjatorami dla klasy. Wyznaczony inicjator w pełni inicjuje wszystkie właściwości wprowadzone przez tę klasę i wywołuje odpowiedni inicjator nadklasy, aby kontynuować proces inicjalizacji w łańcuchu nadklasy.

convenience init:

Wygodne inicjatory są pomocnicze i obsługują inicjatory dla klasy. Można zdefiniować wygodny inicjator, aby wywołać wyznaczony inicjator z tej samej klasy, co inicjator wygody, z niektórymi wyznaczonymi parametrami inicjatora ustawionymi na wartości domyślne. Można również zdefiniować wygodny inicjator, aby utworzyć wystąpienie tej klasy dla określonego przypadku użycia lub typu wartości wejściowej.

zgodnie z dokumentacją Swift

W skrócie, oznacza to, że możesz użyć wygodnego inicjatora, aby wywołanie wyznaczonego inicjatora było szybsze i wygodniejsze. Tak więc wygodne inicjatory wymagają użycia self.initzamiastsuper.init które możesz zobaczyć w przesłonięciu wyznaczonego inicjatora.

przykład pseudokodu:

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)
}

Używam ich często podczas tworzenia niestandardowych widoków i takich, które mają długie inicjatory, głównie z domyślnymi. Doktorzy wykonują lepszą robotę wyjaśniając niż ja, sprawdź ich!

treyhakanson
źródło
73

Wygodne inicjatory są używane, gdy masz jakąś klasę z wieloma właściwościami, co sprawia, że ​​zawsze inicjowanie dowcipu wszystkimi zmiennymi jest trochę "bolesne", więc to, co robisz z wygodnym inicjatorem, polega na tym, że po prostu przekazujesz niektóre zmienne, aby zainicjować obiekt, a pozostałym przypisz wartość domyślną. Na stronie Ray Wenderlich jest bardzo dobry film, nie jestem pewien, czy jest darmowy, czy nie, ponieważ mam płatne konto. Oto przykład, w którym możesz zobaczyć, że zamiast inicjować mój obiekt z tymi wszystkimi zmiennymi, nadaję mu tylko tytuł.

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
źródło
4
Dobry przykład. To wyjaśniło koncepcję. Dziękuję Ci.
Arjun Kalidas,
5
Możemy również mieć init () z domyślnymi wartościami autora i parametru daty jako `init (tytuł: String, autor: String =" Nieznany ", data: Int = 2016) {self.title = title self.author = author self. date = date scenes = [Scene] ()} `to dlaczego potrzebujemy wygodnego inicjatora?
shripad20
@ shripad20 to samo pytanie tutaj. Czy zauważyłeś, dlaczego musimy skorzystać z wygody, chociaż możemy zrobić inny inicjator i wysłać z niego domyślny parametr. Daj mi znać, jeśli znajdziesz odpowiedź
user100
wygoda jest dodatkowa w stosunku do „main” self.init, podczas gdy self.init z domyślnymi parametrami zastępuje „main” self.init. hackingwithswift.com/example-code/language/ ...
user1105951
@ shripad20 sprawdź mój przykład poniżej, aby wyjaśnić, gdzie inicjatory wygody pokonują wartości domyślne. Mam nadzieję, że to pomoże.
Chris Graf
14

Oto prosty przykład zaczerpnięty z portalu Apple Developer .

Zasadniczo wyznaczonym inicjatorem jest inicjator init(name: String), który zapewnia, że ​​wszystkie przechowywane właściwości są inicjowane.

init()Initializer wygoda, biorąc żadnego argumentu, automatycznie ustawia wartość nameprzechowywanego mienia się [Unnamed]za pomocą wyznaczonego inicjatora.

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]"

Jest to przydatne, gdy mamy do czynienia z dużymi klasami, z co najmniej kilkoma przechowywanymi właściwościami. Polecam przeczytać więcej na temat opcji i dziedziczenia w portalu Apple Developer.

dirtydanee
źródło
6

Gdzie wygodne inicjatory pokonują ustawienie domyślnych wartości parametrów

Dla mnie, convenience initializers są przydatne, jeśli jest więcej do zrobienia niż po prostu ustawienie wartości domyślnej dla właściwości klasy.

Implementacja klasy z wyznaczonym init ()

W przeciwnym razie po prostu ustawiłbym wartość domyślną w initdefinicji, np .:

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
    }
}

Rozszerzenie klasy z wygodą init ()

Jednak może być coś więcej do zrobienia niż po prostu ustawienie wartości domyślnej i tutaj jest convenience initializersto przydatne:

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)
    }
}

Przykłady użycia

// 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
źródło
1
dobrze i prosto wyjaśniono tutaj
vivi
4

Wygodny inicjator można zdefiniować w rozszerzeniu klasy . Ale standardowy - nie może.

Serg
źródło
1
Chociaż nie jest to pełna odpowiedź na pytanie, to jest coś, czego jeszcze nie brałem pod uwagę, dzięki!
Marc-André Weibezahn
3

convenience initsprawia, że opcjonalny zainicjować klasę z wartościami.

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

Badr
źródło
3

Ma to sens, jeśli twoim przypadkiem użycia jest wywołanie inicjatora w innym inicjatorze w tej samej klasie .

Spróbuj to zrobić na placu zabaw

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")

Ze słowem kluczowym wygody

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
źródło
2

Uwaga: przeczytaj cały tekst

Wyznaczone inicjatory są podstawowymi inicjatorami dla klasy. Wyznaczony inicjator w pełni inicjuje wszystkie właściwości wprowadzone przez tę klasę i wywołuje odpowiedni inicjator nadklasy, aby kontynuować proces inicjalizacji aż do łańcucha nadklasy.

Wygodne inicjatory są pomocnicze i obsługują inicjatory dla klasy. Można zdefiniować wygodny inicjator, aby wywołać wyznaczony inicjator z tej samej klasy, co wygodny inicjator, z niektórymi wyznaczonymi parametrami inicjatora ustawionymi na wartość domyślną.

Wyznaczone inicjatory dla klas są zapisywane w taki sam sposób, jak proste inicjatory dla typów wartości:

init(parameters) {
statements
}

Wygodne inicjatory są napisane w tym samym stylu, ale z wygodnym modyfikatorem umieszczonym przed słowem kluczowym init, oddzielonym spacją:

convenience init(parameters) {
statements
}

Oto praktyczny przykład:

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”

Inicjator init (name: String) z klasy Food jest dostarczany jako wyznaczony inicjator, ponieważ zapewnia, że ​​wszystkie przechowywane właściwości nowego wystąpienia Food są w pełni zainicjowane. Klasa Food nie ma nadklasy, więc inicjator init (nazwa: String) nie musi wywoływać super.init (), aby zakończyć swoją inicjalizację.

„Klasa Food zapewnia również wygodny inicjator, init (), bez argumentów. Init () inicjalizator zapewnia domyślną nazwę symbolu zastępczego dla nowej żywności, delegując ją do init klasy Food (name: String) z wartością nazwy [Unnamed]: ”

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

Druga klasa w hierarchii to podklasa Food o nazwie RecipeIngredient. Klasa RecipeIngredient modeluje składnik przepisu kulinarnego. Wprowadza właściwość Int o nazwie quantity (oprócz właściwości name, którą dziedziczy z Food) i definiuje dwa inicjatory do tworzenia instancji RecipeIngredient:

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)
}
}

Klasa RecipeIngredient ma jeden wyznaczony inicjator, init (name: String, quantity: Int), którego można użyć do wypełnienia wszystkich właściwości nowego wystąpienia RecipeIngredient. Ten inicjator rozpoczyna się od przypisania przekazanego argumentu ilości do właściwości quantity, która jest jedyną nową właściwością wprowadzoną przez RecipeIngredient. Po wykonaniu tej czynności inicjator deleguje do inicjatora init (name: String) klasy Food.

strona: 536 Fragment From: Apple Inc. „Swift Programming Language (Swift 4)”. iBooks. https://itunes.apple.com/pk/book/the-swift-programming-language-swift-4-0-3/id881256329?mt=11

Abuzar Manzoor
źródło
2

Przydaje się więc, gdy nie musisz określać każdej właściwości dla klasy. Na przykład, jeśli chcę tworzyć wszystkie przygody z początkową wartością HP równą 100, użyłbym tego następującego wygodnego init i po prostu dodałbym nazwę. To znacznie ograniczy kod.

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
źródło
1

Wszystkie odpowiedzi brzmią dobrze, ale zrozummy to na prostym przykładzie

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

Teraz wiemy, że klasa może dziedziczyć inną klasę, więc

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

Teraz, w tym przypadku, tworząc instancję dla klasy Z, będziesz musiał podać obie wartości „a” i „b”.

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

Ale co, jeśli chcesz przekazać tylko wartość b i chcesz, aby reszta przybierała wartość domyślną dla innych, w takim przypadku musisz zainicjować inne wartości jako wartość domyślną. Ale czekaj, jak ?, ponieważ musisz to ustawić wcześniej tylko w klasie.

//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)
}

Teraz możesz tworzyć instancje klasy Z, podając niektóre, wszystkie lub żadne wartości dla zmiennych.

let z1 = Z(b: 2)
let z2 = Z()
shubham mishra
źródło