Mam problemy ze zrozumieniem różnicy między nimi lub celem convenience init
.
swift
initialization
convenience-methods
Chino Pan
źródło
źródło
Odpowiedzi:
Standardowe
init
:convenience init
: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!
źródło
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
źródło
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.
źródło
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
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 } }
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 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) } }
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")
źródło
Wygodny inicjator można zdefiniować w rozszerzeniu klasy . Ale standardowy - nie może.
źródło
convenience init
sprawia, że opcjonalny zainicjować klasę z wartościami.źródło
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! } }
źródło
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
źródło
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) } }
źródło
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()
źródło