W języku Swift, aby zainicjować instancję, należy wypełnić wszystkie pola tej klasy, a dopiero potem wywołać superkonstruktor:
class Base {
var name: String
init(name: String) {
self.name = name
}
}
class Derived: Base {
var number: Int
init(name: String, number: Int) {
// won't compile if interchange lines
self.number = number
super.init(name)
}
}
Dla mnie wydaje się to odwrócone, ponieważ instancja musi self
zostać utworzona przed przypisaniem wartości do jej pól, a ten kod sprawia wrażenie, jakby tworzenie łańcuchów następowało dopiero po przypisaniu. Poza tym nadklasa nie ma legalnych możliwości odczytania wprowadzonych atrybutów podklasy, więc bezpieczeństwo nie ma w tym przypadku znaczenia.
Ponadto wiele innych języków, takich jak JavaScript, a nawet Cel C, który jest nieco duchowym przodkiem Swifta, wymaga połączenia łańcuchowego przed uzyskaniem dostępu self
, a nie później.
Jakie jest uzasadnienie tego wyboru, aby wymagać zdefiniowania pól przed wywołaniem superkonstruktora?
źródło
self
.Odpowiedzi:
W C ++, gdy tworzysz obiekt pochodny, zaczyna się on jako obiekt podstawowy podczas działania konstruktora podstawowego, więc w czasie działania konstruktora podstawowego członkowie pochodne nawet nie istnieją. Dlatego nie trzeba ich inicjować i nie można ich inicjować. Dopiero po zakończeniu konstruktora Base obiekt zmieni się w obiekt pochodny z dużą ilością niezainicjowanych pól, które następnie zainicjujesz.
W Swift po utworzeniu obiektu pochodnego jest to obiekt pochodny od samego początku. Jeśli metody zostaną zastąpione, wówczas podstawowa metoda inicjująca będzie już korzystać z zastąpionych metod, które mogą uzyskać dostęp do pochodnych zmiennych składowych. Dlatego wszystkie pochodne zmienne składowe należy zainicjować przed wywołaniem podstawowej metody init.
PS. Wspomniałeś o Cel-C. W Objective-C wszystko jest automatycznie inicjowane na 0 / zero / NO. Ale jeśli ta wartość nie jest poprawną wartością do inicjalizacji zmiennej, wówczas podstawowa metoda init może łatwo wywołać metodę, która jest nadpisana i używa jeszcze niezainicjowanej zmiennej o wartości 0 zamiast prawidłowej wartości. W Objective-C nie jest to naruszenie reguł językowych (tak definiuje się działanie), ale oczywiście błąd w kodzie. W Swift ten błąd nie jest dozwolony przez język.
PS. Istnieje komentarz „czy jest to obiekt pochodny od samego początku, czy też nie można go zaobserwować z powodu reguł językowych”? Klasa Derived zainicjowała swoich członków przed wywołaniem podstawowej metody init, a ci członkowie Derived zachowują swoje wartości. Tak więc albo jest to obiekt pochodny w momencie wywołania inicjalizacji Base, albo kompilator musiałby zrobić coś dziwnego. I zaraz po zainicjowaniu przez metodę inicjującą Base wszystkich elementów instancji Base, może ona wywoływać zastąpione funkcje, co udowodni, że jest to instancja klasy pochodnej.
źródło
Wynika to z zasad bezpieczeństwa Swift, jak wyjaśniono w sekcji Inicjalizacja dwufazowa na stronie Inicjalizacja dokumentu językowego .
Zapewnia, że każde pole jest ustawione przed użyciem (szczególnie wskaźniki, aby uniknąć awarii).
Swift osiąga to dzięki dwufazowej sekwencji inicjalizacji: każdy inicjator musi zainicjować wszystkie swoje pola instancji, a następnie wywołać inicjator nadklasy, aby zrobić to samo, i dopiero po tym, jak to się stanie na drzewie, te inicjalizatory pozwolą na
self
ucieczkę wskaźnika, wywołanie instancji metody lub przeczytaj wartości właściwości instancji.Następnie mogą dokonać dalszej inicjalizacji, upewniając się, że obiekt jest dobrze uformowany. W szczególności wszystkie nie opcjonalne wskaźniki będą miały prawidłowe wartości. zero nie jest dla nich ważne.
Cel C nie różni się zbytnio od tego, że 0 lub zero jest zawsze prawidłową wartością, więc inicjator pierwszej fazy jest wykonywany przez alokator ustawiając wszystkie pola na 0. Ponadto, Swift ma niezmienne pola, więc muszą zostać zainicjowane w fazie pierwszej . A Swift egzekwuje te zasady bezpieczeństwa.
źródło
Rozważać
Dlatego nie ma prostej konstrukcji, która zapewniłaby kontrahentowi bezpieczeństwo, gdy dozwolone są metody wirtualne , Swift unika tych problemów, wymagając inicjalizacji dwufazowej, zapewniając w ten sposób większe bezpieczeństwo programistom, a jednocześnie tworząc bardziej złożony język.
Jeśli możesz rozwiązać te problemy w przyjemny sposób, nie zaliczaj „Idź”, przejdź bezpośrednio do kolekcji swojego PHd…
źródło