Mam dwie klasy Shape
iSquare
class Shape {
var numberOfSides = 0
var name: String
init(name:String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
super.init(name:name) // Error here
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Przy powyższej implementacji pojawia się błąd:
property 'self.sideLength' not initialized at super.init call
super.init(name:name)
Dlaczego muszę ustawić self.sideLength
przed połączeniem super.init
?
properties
compiler-errors
swift
JuJoDi
źródło
źródło
Odpowiedzi:
Cytat z The Swift Programming Language, który odpowiada na twoje pytanie:
źródło
init
.Swift ma bardzo wyraźną, określoną sekwencję operacji wykonywanych w inicjalizatorach. Zacznijmy od kilku podstawowych przykładów i przejdźmy do ogólnej sprawy.
Weźmy obiekt A. Zdefiniujemy go w następujący sposób.
Zauważ, że A nie ma nadklasy, więc nie może wywołać funkcji super.init (), ponieważ ona nie istnieje.
OK, więc teraz podklasę A z nową klasą o nazwie B.
Jest to odejście od celu C, w którym
[super init]
zwykle nazywa się go najpierw przed czymkolwiek innym. Nie tak w Swift. Jesteś odpowiedzialny za upewnienie się, że zmienne instancji są w spójnym stanie, zanim zrobisz cokolwiek innego, w tym metody wywoływania (w tym inicjalizator nadklasy).źródło
Z dokumentów
Dlaczego potrzebujemy takiej kontroli bezpieczeństwa?
Aby odpowiedzieć na to, przejdźmy szybko przez proces inicjalizacji.
Tak więc, aby upewnić się, że dwuetapowy proces inicjalizacji przebiega zgodnie z powyższą definicją, istnieją cztery kontrole bezpieczeństwa, jednym z nich jest
Teraz dwufazowa inicjalizacja nigdy nie mówi o kolejności, ale ta kontrola bezpieczeństwa wprowadza się
super.init
po zamówieniu, po inicjalizacji wszystkich właściwości.Kontrola bezpieczeństwa 1 może wydawać się nieistotna, ponieważ inicjalizacja dwufazowa uniemożliwia dostęp do wartości właściwości przed ich zainicjowaniem , bez tej kontroli bezpieczeństwa 1.
Jak w tej próbce
Triangle.init
zainicjował każdą właściwość przed użyciem. Kontrola bezpieczeństwa 1 wydaje się nieistotna,Ale potem może być inny scenariusz, trochę skomplikowany,
Wynik :
Tutaj, gdybyśmy wywołali
super.init
przed ustawieniemhypotenuse
,super.init
wywołanie wywołałoby wtedyprintShapeDescription()
i, ponieważ zostało to nadpisane, najpierw powróciłoby do implementacji klasy TrójkątaprintShapeDescription()
.printShapeDescription()
Triangle dostępu klasowymhypotenuse
niebędącego własnością opcjonalne, które wciąż nie zostały zainicjowane zostały. Nie jest to dozwolone, ponieważ inicjalizacja dwufazowa uniemożliwia dostęp do wartości właściwości przed ich zainicjowaniemUpewnij się więc, że inicjalizacja dwufazowa przebiega zgodnie z definicją, musi istnieć konkretna kolejność wywoływania
super.init
, to znaczy po zainicjowaniu wszystkich właściwości wprowadzonych przezself
klasę, dlatego potrzebujemy kontroli bezpieczeństwa 1źródło
init
może wywoływać funkcję (przesłaniającą) ... w której ta funkcja uzyskuje dostęp do właściwości podklas, a następnie, aby uniknąć braku ustawiania wartości, wywołaniesuper
musi nastąpić po ustawieniu wszystkich wartości. OK ma sens. Zastanawiacie się, jak to zrobiło Objective C i dlaczego musieliściesuper
najpierw zadzwonić ?printShapeDescription()
przedself.sides = sides; self.name = named;
który generuje ten błąd:use of 'self' in method call 'printShapeDescription' before all stored properties are initialized
. Błąd PO jest podawany w celu zmniejszenia „możliwości” błędu czasu wykonywania.printShapeDescription
to funkcja, do której się nie odwoływało,self
tzn. Byłaby czymś w rodzaju „print („ nic ”), to nie byłoby problemów. (Jednak nawet z tego powodu kompilator zgłasza błąd, ponieważ nie jest super inteligentny)„Super.init ()” należy wywołać po zainicjowaniu wszystkich zmiennych instancji.
W filmie Apple „Intermediate Swift” (można go znaleźć na stronie zasobów wideo Apple Developer https://developer.apple.com/videos/wwdc/2014/ ), około 28:40, wyraźnie zaznaczono, że wszystkie inicjatory w superklasa musi być nazwana PO zainicjowaniu zmiennych instancji.
W Objective-C było odwrotnie. W Swift, ponieważ wszystkie właściwości muszą zostać zainicjowane przed ich użyciem, musimy najpierw zainicjować właściwości. Ma to na celu zapobieganie wywołaniu funkcji przesłoniętej z metody „init ()” superklasy, bez uprzedniego inicjowania właściwości.
Tak więc implementacja „Square” powinna być:
źródło
Przepraszamy za brzydkie formatowanie. Wystarczy umieścić znak zapytania po deklaracji i wszystko będzie dobrze. Pytanie mówi kompilatorowi, że wartość jest opcjonalna.
Edycja1:
Jest lepszy sposób na pominięcie tego błędu. Zgodnie z komentarzem jmaschada nie ma powodu, aby używać opcjonalnego w twoim przypadku, ponieważ opcjonalne nie są wygodne w użyciu i zawsze musisz sprawdzić, czy opcjonalne nie jest zerowe przed uzyskaniem dostępu. Musisz tylko zainicjować członka po deklaracji:
Edycja2:
Po dwóch minusach tej odpowiedzi znalazłem jeszcze lepszy sposób. Jeśli chcesz, aby członek klasy został zainicjowany w konstruktorze, musisz przypisać mu wartość początkową wewnątrz contructor i przed wywołaniem super.init (). Lubię to:
Powodzenia w nauce Swift.
źródło
super.init(name:name)
iself.sideLength = sideLength
. DeklarowaniesideLength
jako opcja jest misguiding i wprowadza dodatkowe kłopotów później, kiedy trzeba wymusić unwrap niego.var sideLength: Double
, bez potrzeby przypisywania jej wartości początkowejswift wymusza inicjalizację każdego członka var, zanim będzie on kiedykolwiek / mógł zostać użyty. Ponieważ nie można mieć pewności, co się stanie, gdy zostanie włączony supers, błędnie się sprawdza: lepiej zabezpieczyć niż przepraszać
źródło
super.init
w pierwszej linii?Edward,
Możesz zmodyfikować kod w swoim przykładzie w następujący sposób:
Wykorzystuje to domyślnie nieopakowane opcjonalne.
W dokumentacji możemy przeczytać:
źródło
Swift nie pozwoli ci zainicjować superklasy bez inicjowania właściwości, odwrotnie niż w Obj C. Więc musisz zainicjować wszystkie właściwości przed wywołaniem „super.init”.
Przejdź do http://blog.scottlogic.com/2014/11/20/swift-initialisation.html . To daje dobre wyjaśnienie twojego problemu.
źródło
Dodaj zero na końcu deklaracji.
To zadziałało w moim przypadku, ale może nie działać w twoim przypadku
źródło
Po prostu inicjujesz w niewłaściwej kolejności.
źródło
Prawdopodobnie otrzymam kilka głosów negatywnych, ale szczerze mówiąc, życie jest łatwiejsze w ten sposób:
źródło
To zadziwiająco głupi projekt.
Rozważ coś takiego:
Jest to nieprawidłowe, jak wspomniano powyżej. Ale tak jest:
Ponieważ „ja” nie zostało zainicjowane.
Mam szczerą nadzieję, że ten błąd zostanie wkrótce naprawiony.
(Tak, wiem, że mógłbym stworzyć pusty obiekt, a następnie ustawić rozmiar, ale to po prostu głupie).
źródło