Cos'è esattamente init coder aDecoder?


122

Sto imparando lo sviluppo iOS da un corso online e ogni volta che creo una visualizzazione personalizzata (cella di visualizzazione tabella personalizzata, cella di visualizzazione raccolta, ecc.) L'istruttore implementa sempre questo inizializzatore:

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

Perché esattamente devo chiamarlo sempre? Che cosa fa? Posso inserire delle proprietà nell'init?


5
Questa risposta vi aiuterà a stackoverflow.com/questions/24036393/... Grazie
Seungyoun Yi

2
Se si sottoclasse un oggetto che implementa, NSCodingè necessario implementare questo inizializzatore, poiché è richiesto dalle classi che implementano NSCoding. Devi almeno chiamare il metodo init della superclasse. Se NSCodercontiene proprietà codificate per la tua classe, puoi utilizzare questo metodo per recuperarle
Paulw11

1
Inoltre, ti consiglio di leggere la sezione sull'inizializzazione degli oggetti nel libro ufficiale di Swift di Apple.
Nicolas Miari

Risposte:


121

Inizierò questa risposta dalla direzione opposta: cosa succede se si desidera salvare lo stato della visualizzazione su disco? Questo è noto come serializzazione . Il contrario è la deserializzazione , ovvero il ripristino dello stato dell'oggetto dal disco.

Il NSCodingprotocollo definisce due metodi per serializzare e deserializzare gli oggetti:

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

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

Allora perché è necessario nella tua classe personalizzata? La risposta è Interface Builder. Quando si trascina un oggetto su uno storyboard e lo si configura, Interface Builder serializza lo stato di quell'oggetto su disco, quindi lo deserializza quando lo storyboard appare sullo schermo. Devi dire a Interface Builder come fare quelli. Per lo meno, se non aggiungi nuove proprietà alla tua sottoclasse, puoi semplicemente chiedere alla superclasse di fare il pacchetto e il disimballaggio per te, da qui ilsuper.init(coder: aDecoder) chiamata. Se la tua sottoclasse è più complessa, devi aggiungere il tuo codice di serializzazione e deserializzazione per la sottoclasse.

Ciò è in contrasto con l'approccio di Visual Studio, che consiste nello scrivere codice in un file nascosto per creare l'oggetto in fase di esecuzione.


Perché non mettere tutto all'interno di awakeFromNib e dimenticare di usarlo init(coder aCoder : NSCoder)?
Miele

@ Miele - in una parola, "a volte non puoi farlo". Di solito puoi ma non sempre.
Fattie

@Fattie i dettagli di non farlo sono troppo complessi o inutili da sapere? Se non ti dispiace spiegare?
Miele

9
@Honey, se vuoi configurare il tuo oggetto in Interface Builder, allora awakeFromNibnon funzionerà. awakeFromNibviene richiamato in fase di esecuzione . Tutto ciò che fai in Interface Builder è durante la fase di progettazione . Per portare ciò che hai fatto in fase di progettazione al tempo di esecuzione è encodeWithCoder(salvataggio) e init(coder:)(caricamento)
Codice diverso

3
@Honey se non usi Interface Builder per configurare la tua classe personalizzata (es. Fallo a livello di codice con il codice), puoi farlo in awakeFromNiboinitWIthFrame
Codice diverso

28

Il requisito per implementare quell'inizializzatore è una conseguenza di due cose:

  1. Il principio di sostituzione di Liskov . Se S è una sottoclasse di T (ad esempio MyViewControllerè una sottoclasse di ViewController), allora gli oggetti S (istanze di MyViewController) devono poter essere sostituiti laddove ViewControllersono attesi oggetti T (istanze di ).

  2. Gli inizializzatori non vengono ereditati in Swift se alcuni inizializzatori sono definiti esplicitamente nella sottoclasse. Se un inizializzatore viene fornito esplicitamente, tutti gli altri devono essere forniti esplicitamente (che possono quindi essere semplicemente chiamati super.init(...)). Vedi questa domanda per la logica. È in Java, ma è ancora valido.

Al punto 1, tutto ciò che l'originale ViewControllerpuò fare, la MyViewControllersottoclasse dovrebbe essere in grado di farlo. Una di queste cose è poter essere inizializzato da un datoNSCoder . Al punto 2, la tua MyViewControllersottoclasse non eredita automaticamente questa capacità. Pertanto, è necessario fornire manualmente l'inizializzatore che soddisfa questo requisito. In questo caso, devi solo delegare alla superclasse, affinché faccia ciò che normalmente farebbe.


1
È perfettamente logico che i costruttori non vengano ereditati: se si inizializza un'istanza della classe derivata utilizzando l'inizializzatore (ereditato) della classe base, le proprietà non ereditate che sono state definite ("aggiunte") dalla classe derivata non verranno mai essere inizializzato.
Nicolas Miari

3
In realtà, gli inizializzatori vengono ereditati in Swift, dato che non fornisci alcuna implementazione degli inizializzatori nella tua sottoclasse. Se le proprietà non ereditate appena definite hanno valori predefiniti, puoi cavartela senza scrivere alcun inizializzatore nella tua sottoclasse ed ereditare semplicemente tutti gli inizializzatori della tua superclasse. Vedi qui
TheBaj
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.