Czym dokładnie jest koder init aDecoder?

122

Uczę się programowania na iOS z kursu online i za każdym razem, gdy tworzę niestandardowy widok (niestandardowa komórka widoku tabeli, komórka widoku kolekcji itp.), Instruktor zawsze implementuje ten inicjator:

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

Dlaczego właściwie zawsze muszę to nazywać? Co to robi? Czy mogę umieścić właściwości wewnątrz init?

JasonP
źródło
5
Ta odpowiedź pomoże Ci stackoverflow.com/questions/24036393/
....
2
Jeśli podklasujesz obiekt, który implementuje NSCoding, musisz zaimplementować ten inicjator, ponieważ jest to wymagane od klas, które implementują NSCoding. Musisz przynajmniej wywołać metodę init nadklasy. Jeśli NSCoderzawiera zakodowane właściwości dla Twojej klasy, możesz użyć tej metody, aby je odzyskać
Paulw11
1
Polecam również przeczytanie sekcji o inicjalizacji obiektów w oficjalnej książce Swift autorstwa Apple.
Nicolas Miari,

Odpowiedzi:

121

Zacznę od tej odpowiedzi w przeciwnym kierunku: co jeśli chcesz zapisać stan swojego widoku na dysku? Jest to znane jako serializacja . Odwrotna sytuacja to deserializacja - przywrócenie stanu obiektu z dysku.

NSCodingProtokół określa dwa sposoby serializacji i odserializacji obiektów:

encodeWithCoder(_ aCoder: NSCoder) {
    // Serialize your object here
}

init(coder aDecoder: NSCoder) {
    // Deserialize your object here
}

Dlaczego więc jest potrzebny w Twojej klasie niestandardowej? Odpowiedzią jest Interface Builder. Po przeciągnięciu obiektu na scenorys i skonfigurowaniu go, Interface Builder serializuje stan tego obiektu na dysk, a następnie deserializuje go, gdy scenorys pojawia się na ekranie. Musisz powiedzieć Interface Builder, jak to zrobić. Przynajmniej, jeśli nie dodasz żadnych nowych właściwości do swojej podklasy, możesz po prostu poprosić superklasę o wykonanie pakowania i rozpakowywania za Ciebie, stąd super.init(coder: aDecoder)wezwanie. Jeśli Twoja podklasa jest bardziej złożona, musisz dodać własny kod serializacji i deserializacji dla podklasy.

Jest to przeciwieństwo podejścia Visual Studio, które polega na zapisaniu kodu w ukrytym pliku w celu utworzenia obiektu w czasie wykonywania.

Kod inny
źródło
Dlaczego nie umieścić wszystkiego w środku awakeFromNib i zapomnieć o użyciu init(coder aCoder : NSCoder)?
Miód
@ Kochanie - jednym słowem „czasami nie możesz tego zrobić”. Zwykle możesz, ale nie zawsze.
Fattie
@Fattie czy szczegóły tego, jak nie robić tego, są zbyt skomplikowane lub niepotrzebne? Jeśli nie, czy możesz wyjaśnić?
Miód
9
@ Kochanie, jeśli chcesz skonfigurować swój obiekt w Interface Builder, to awakeFromNibnie zadziała. awakeFromNibjest wywoływana w czasie wykonywania . Wszystko, co robisz w programie Interface Builder, ma miejsce podczas projektowania . Aby przenieść to, co zrobiłeś w czasie projektowania, do czasu uruchomienia, jest encodeWithCoder(oszczędzanie) i init(coder:)(ładowanie)
Kod inny
3
@ Kochanie, jeśli nie używasz Interface Builder do konfigurowania własnej klasy (tj. Zrób to programowo z kodem), możesz to zrobić w awakeFromNiblubinitWIthFrame
Code Different
28

Wymóg zaimplementowania tego inicjatora jest konsekwencją dwóch rzeczy:

  1. Zasada substytucji Liskov . Jeśli S jest podklasą T (np. MyViewControllerJest podklasą ViewController), to S obiektów (instancji MyViewController) musi mieć możliwość zastąpienia tam, gdzie ViewControlleroczekuje się T obiektów (instancji ).

  2. Inicjatory nie są dziedziczone w języku Swift, jeśli jakiekolwiek inicjatory są jawnie zdefiniowane w podklasie. Jeśli jeden inicjator jest jawnie podany, wszystkie inne muszą być jawnie podane (co może wtedy po prostu wywołać super.init(...)). Zobacz to pytanie dla uzasadnienia. Jest w Javie, ale nadal obowiązuje.

W punkcie 1, wszystko, co ViewControllermoże zrobić oryginał , MyViewControllerpowinna być w stanie zrobić podklasa. Jedną z takich rzeczy jest możliwość inicjalizacji z danego NSCoder. W punkcie 2 twoja MyViewControllerpodklasa nie dziedziczy automatycznie tej zdolności. W związku z tym należy ręcznie podać inicjator, który spełnia to wymaganie. W takim przypadku wystarczy oddelegować zadanie do superklasy, aby zrobiła to, co zwykle.

Alexander - Przywróć Monikę
źródło
1
Ma to sens, że konstruktory nie są dziedziczone: jeśli zainicjujesz wystąpienie klasy pochodnej za pomocą (dziedziczonego) inicjatora klasy bazowej, niedziedziczone właściwości, które zostały nowo zdefiniowane („dodane”) przez klasę pochodną, ​​nigdy nie będą być zainicjowane.
Nicolas Miari,
3
W rzeczywistości inicjatory są dziedziczone w języku Swift, biorąc pod uwagę, że nie dostarczasz żadnych własnych implementacji inicjatorów w podklasie. Jeśli nowo zdefiniowane, niedziedziczone właściwości mają wartości domyślne, możesz uciec od niepisania żadnych inicjatorów w podklasie i po prostu odziedziczyć wszystkie inicjatory z nadklasy. Zobacz tutaj
TheBaj