Mam problemy ze zrozumieniem różnicy między nimi lub celem convenience init
.
Mam problemy ze zrozumieniem różnicy między nimi lub celem convenience init
.
Odpowiedzi:
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.init
zamiastsuper.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!
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
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ść name
przechowywanego 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.
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.
W przeciwnym razie po prostu ustawiłbym wartość domyślną w init
definicji, 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
}
}
Jednak może być coś więcej do zrobienia niż po prostu ustawienie wartości domyślnej i tutaj jest convenience initializers
to 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)
}
}
// 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")
Wygodny inicjator można zdefiniować w rozszerzeniu klasy . Ale standardowy - nie może.
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!
}
}
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
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)
}
}
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()