Istniejące odpowiedzi mówią tylko połowę conveniencehistorii. Druga połowa historii, połowa, której żadna z istniejących odpowiedzi nie obejmuje, odpowiada na pytanie, które Desmond zamieścił w komentarzach:
Dlaczego Swift miałby zmuszać mnie do umieszczenia convenienceprzed moim inicjatorem tylko dlatego, że muszę self.initz niego zadzwonić ? `
Poruszyłem go nieco w tej odpowiedzi , w której szczegółowo omawiam kilka reguł inicjowania języka Swift, ale główny nacisk położono na requiredsłowo. Ale ta odpowiedź wciąż odnosiła się do czegoś, co jest istotne dla tego pytania i tej odpowiedzi. Musimy zrozumieć, jak działa dziedziczenie inicjalizatora Swift.
Ponieważ Swift nie zezwala na niezainicjowane zmienne, nie ma gwarancji, że odziedziczysz wszystkie (lub jakiekolwiek) inicjatory z klasy, z której dziedziczysz. Jeśli utworzymy podklasę i dodamy jakiekolwiek niezainicjowane zmienne instancji do naszej podklasy, przestaliśmy dziedziczyć inicjatory. Dopóki nie dodamy własnych inicjatorów, kompilator będzie na nas krzyczał.
Dla jasności, niezainicjowana zmienna instancji to dowolna zmienna instancji, której nie nadano wartości domyślnej (należy pamiętać, że opcje i niejawnie rozpakowane opcje opcjonalne automatycznie przyjmują wartość domyślną nil).
Więc w tym przypadku:
class Foo {
var a: Int
}
ajest niezainicjowaną zmienną instancji. To się nie skompiluje, chyba że podamy awartość domyślną:
class Foo {
var a: Int = 0
}
lub zainicjuj aw metodzie inicjalizującej:
class Foo {
var a: Int
init(a: Int) {
self.a = a
}
}
Zobaczmy teraz, co się stanie, jeśli podklasujemy Foo, dobrze?
class Bar: Foo {
var b: Int
init(a: Int, b: Int) {
self.b = b
super.init(a: a)
}
}
Dobrze? Dodaliśmy zmienną i dodaliśmy inicjalizator, aby ustawić wartość, aby bmogła się skompilować. W zależności od tego, co język idziesz z, można spodziewać się, że Barodziedziczył Foo„s inicjator, init(a: Int). Ale tak nie jest. A jak to możliwe? Jak to Foojest init(a: Int)wiedza, jak przypisać wartość do bzmiennej, która Bardodana? Tak nie jest. Dlatego nie możemy zainicjować Barinstancji za pomocą inicjatora, który nie może zainicjować wszystkich naszych wartości.
Co to wszystko ma wspólnego convenience?
Przyjrzyjmy się regułom dziedziczenia inicjatorów :
Zasada nr 1
Jeśli twoja podklasa nie definiuje żadnych wyznaczonych inicjatorów, automatycznie dziedziczy wszystkie wyznaczone inicjatory nadklasy.
Zasada 2
Jeśli Twoja podklasa zapewnia implementację wszystkich wyznaczonych przez nią inicjatorów nadklasy - albo przez dziedziczenie ich zgodnie z regułą 1, albo przez dostarczenie niestandardowej implementacji jako części swojej definicji - wówczas automatycznie dziedziczy wszystkie inicjatory wygody nadklasy.
Zwróć uwagę na regułę 2, która wspomina o wygodnych inicjatorach.
Więc co convenienceHasło nie zrobić, to wskazać nam inicjalizatory które mogą być dziedziczone przez podklasy, że zmienne instancji dodatek bez wartości domyślnych.
Weźmy przykładową Baseklasę:
class Base {
let a: Int
let b: Int
init(a: Int, b: Int) {
self.a = a
self.b = b
}
convenience init() {
self.init(a: 0, b: 0)
}
convenience init(a: Int) {
self.init(a: a, b: 0)
}
convenience init(b: Int) {
self.init(a: 0, b: b)
}
}
Zauważ, że mamy conveniencetutaj trzy inicjatory. Oznacza to, że mamy trzy inicjatory, które mogą być dziedziczone. Mamy jeden wyznaczony inicjator (wyznaczony inicjator to po prostu dowolny inicjalizator, który nie jest wygodnym inicjatorem).
Instancje klasy bazowej możemy utworzyć na cztery różne sposoby:

Stwórzmy więc podklasę.
class NonInheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
}
Dziedziczymy z Base. Dodaliśmy własną zmienną instancji i nie nadaliśmy jej wartości domyślnej, więc musimy dodać własne inicjatory. Dodaliśmy jeden, init(a: Int, b: Int, c: Int)ale nie pasuje podpis Baseklasy jest wyznaczony Inicjator: init(a: Int, b: Int). Oznacza to, że nie dziedziczymy żadnych inicjatorów z Base:

Więc co by się stało, gdybyśmy odziedziczyli po Base, ale poszliśmy dalej i zaimplementowaliśmy inicjator, który pasuje do wyznaczonego inicjatora z Base?
class Inheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
convenience override init(a: Int, b: Int) {
self.init(a: a, b: b, c: 0)
}
}
Teraz, oprócz dwóch inicjatorów, które zaimplementowaliśmy bezpośrednio w tej klasie, ponieważ zaimplementowaliśmy inicjator pasujący Basedo wyznaczonego inicjatora klasy, możemy dziedziczyć wszystkie inicjatory Baseklasy convenience:

Fakt, że inicjator z pasującym podpisem jest oznaczony jako, conveniencenie ma tutaj znaczenia. Oznacza to tylko, że Inheritorma tylko jeden wyznaczony inicjator. Więc gdybyśmy dziedziczyli z Inheritor, musielibyśmy po prostu zaimplementować ten jeden wyznaczony inicjator, a następnie odziedziczylibyśmy Inheritorwygodny inicjator, co z kolei oznacza, że zaimplementowaliśmy wszystkie Basewyznaczone inicjatory i możemy dziedziczyć jego convenienceinicjatory.