Rozważ dwie klasy:
class A {
var x: Int
init(x: Int) {
self.x = x
}
convenience init() {
self.init(x: 0)
}
}
class B: A {
init() {
super.init() // Error: Must call a designated initializer of the superclass 'A'
}
}
Nie rozumiem, dlaczego to nie jest dozwolone. Ostatecznie wyznaczony inicjator każdej klasy jest wywoływany z dowolnymi potrzebnymi wartościami, więc po co mam powtarzać się w ciągu B
, init
określając x
ponownie wartość domyślną , kiedy wygoda init
w programie A
wystarczy?
class
initialization
swift
Robert
źródło
źródło
Odpowiedzi:
To jest reguła 1 reguł „Inicjalizatora łańcuchowego”, jak określono w Przewodniku programowania Swift, która brzmi:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html
Podkreśl moje. Wyznaczone inicjatory nie mogą wywoływać wygodnych inicjatorów.
Istnieje diagram zgodny z regułami, który pokazuje, jakie „wskazówki” inicjatora są dozwolone:
źródło
SCNGeometry
: możesz dodaćSCNGeometryElement
s tylko za pomocą wygodnego inicjatora, dlatego nie można po nim dziedziczyć.Rozważać
class A { var a: Int var b: Int init (a: Int, b: Int) { print("Entering A.init(a,b)") self.a = a; self.b = b } convenience init(a: Int) { print("Entering A.init(a)") self.init(a: a, b: 0) } convenience init() { print("Entering A.init()") self.init(a:0) } } class B : A { var c: Int override init(a: Int, b: Int) { print("Entering B.init(a,b)") self.c = 0; super.init(a: a, b: b) } } var b = B()
Ponieważ wszystkie wyznaczone inicjatory klasy A są nadpisywane, klasa B odziedziczy wszystkie dogodne inicjatory z A.Więc wykonanie tego da wynik
Entering A.init() Entering A.init(a:) Entering B.init(a:,b:) Entering A.init(a:,b:)
Teraz, gdyby wyznaczony inicjator B.init (a: b :) miałby możliwość wywołania wygodnego inicjatora klasy bazowej A.init (a :), spowodowałoby to rekurencyjne wywołanie B.init (a:, b: ).
źródło
Dzieje się tak, ponieważ możesz skończyć z nieskończoną rekurencją. Rozważać:
class SuperClass { init() { } convenience init(value: Int) { // calls init() of the current class // so init() for SubClass if the instance // is a SubClass self.init() } } class SubClass : SuperClass { override init() { super.init(value: 10) } }
i spójrz na:
let a = SubClass()
który zawoła,
SubClass.init()
który zawoła,SuperClass.init(value:)
który zawołaSubClass.init()
.Wyznaczone / wygodne reguły inicjalizacji są zaprojektowane tak, aby inicjalizacja klasy zawsze była poprawna.
źródło
Znalazłem sposób na to. Nie jest super ładny, ale rozwiązuje problem braku znajomości wartości nadklasy lub chęci ustawienia wartości domyślnych.
Wszystko, co musisz zrobić, to utworzyć instancję nadklasy, korzystając z wygody
init
, bezpośrednio winit
podklasie. Następnie dzwonisz do wyznaczonegoinit
super za pomocą właśnie utworzonej instancji.class A { var x: Int init(x: Int) { self.x = x } convenience init() { self.init(x: 0) } } class B: A { init() { // calls A's convenience init, gets instance of A with default x value let intermediate = A() super.init(x: intermediate.x) } }
źródło
Rozważ wyodrębnienie kodu inicjalizacji z wygodnej
init()
do nowej funkcji pomocniczejfoo()
, wywołaj,foo(...)
aby wykonać inicjalizację w swojej podklasie.źródło
Obejrzyj wideo WWDC „403 middle Swift” o godzinie 18:30, aby uzyskać szczegółowe wyjaśnienie inicjatorów i ich dziedziczenia. Jak to zrozumiałem, rozważ następujące kwestie:
class Dragon { var legs: Int var isFlying: Bool init(legs: Int, isFlying: Bool) { self.legs = legs self.isFlying = isFlying } convenience initWyvern() { self.init(legs: 2, isFlying: true) } }
Ale teraz rozważ podklasę Wyrm: Wyrm to smok bez nóg i skrzydeł. Więc inicjator dla wiwerny (2 nogi, 2 skrzydła) jest do tego zły! Tego błędu można uniknąć, jeśli wygodnej funkcji Wyvern-Initializer po prostu nie można wywołać, ale tylko w pełni wyznaczony inicjator:
class Wyrm: Dragon { init() { super.init(legs: 0, isFlying: false) } }
źródło
initWyvern
wywołanie ma sens?Wyrm
na przeszkodzie, aby zastąpić liczbę odnóg po wywołaniu inicjatora wygody.Dlaczego nie masz po prostu dwóch inicjatorów - jednego z wartością domyślną?
class A { var x: Int init(x: Int) { self.x = x } init() { self.x = 0 } } class B: A { override init() { super.init() // Do something else } } let s = B() s.x // 0
źródło