Perché Swift inizializza sottoclassi i campi appropriati per primo?

7

Nella lingua Swift, per inizializzare un'istanza, è necessario compilare tutti i campi di quella classe e solo successivamente chiamare il supercostruttore:

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)
    }
}

Per me sembra indietro, perché l'istanza ha bisogno di self per essere creata prima di assegnare valori ai suoi campi, e quel codice dà un'impressione come se il concatenamento avvenga solo dopo l'assegnazione. A parte questo, la superclasse non ha mezzi legali per leggere gli attributi introdotti dalla sottoclasse, quindi la sicurezza non conta in questo caso.

Inoltre, molti altri linguaggi, come JavaScript e persino l'Objective C, che è in qualche modo un antenato spirituale di Swift, richiedono una chiamata di concatenamento prima di accedere a self , non dopo.

Qual è il ragionamento alla base di questa scelta per richiedere la definizione dei campi prima di chiamare il supercostruttore?

    
posta Zomagk 26.08.2016 - 08:07
fonte

3 risposte

7

In C ++, quando si crea un oggetto derivato, viene avviato come oggetto Base mentre è in esecuzione il costruttore Base, quindi al momento dell'esecuzione del costruttore Base, i membri derivati non esistono nemmeno. Quindi non hanno bisogno di essere inizializzati e non possono essere inizializzati. Solo quando il costruttore Base ha finito, l'oggetto è cambiato in un oggetto derivato con molti campi non inizializzati che vengono quindi inizializzati.

In Swift, quando si crea un oggetto derivato, si tratta di un oggetto derivato dall'inizio. Se i metodi vengono sovrascritti, il metodo Base init utilizzerà già i metodi sottoposti a override, che potrebbero accedere alle variabili membro derivate. Pertanto tutte le variabili membro derivate devono essere inizializzate prima che venga chiamato il metodo di base di init.

PS. Hai menzionato l'Objective-C. In Objective-C, tutto viene automaticamente inizializzato su 0 / nil / NO. Ma se quel valore non è il valore corretto per inizializzare una variabile, allora il metodo Base init potrebbe facilmente chiamare un metodo che è sovrascritto e usa la variabile non ancora inizializzata con un valore di 0 invece del valore corretto. In Objective-C, non si tratta di una violazione delle regole del linguaggio (è così che viene definito funzionare), ma ovviamente di un bug nel codice. In Swift, quel bug non è permesso dalla lingua.

    
risposta data 26.08.2016 - 10:16
fonte
6

Questo deriva dalle regole di sicurezza di Swift, come spiegato nella sezione Inizializzazione in due fasi nella lingua del documento Inizializzazione della pagina .

Assicura che ogni campo sia impostato prima dell'uso (in particolare puntatori, per evitare arresti anomali).

Swift raggiunge questo con una sequenza di inizializzazione a due fasi: ogni inizializzatore deve inizializzare tutti i suoi campi di istanza, quindi chiamare un inizializzatore di superclasse per fare allo stesso modo, e solo dopo che succede sull'albero questi inizializzatori sono autorizzati a lasciare il self pointer escape, chiama i metodi di istanza o legge i valori delle proprietà dell'istanza.

Possono quindi eseguire ulteriori inizializzazioni, assicurandosi che l'oggetto sia ben formato. In particolare, tutti i puntatori non opzionali avranno valori validi. nil non è valido per loro.

L'obiettivo C non è molto diverso, ad eccezione del fatto che 0 o nil è sempre un valore valido, quindi l'inizializzazione della prima fase viene eseguita dall'allocatore che imposta tutti i campi su 0. Inoltre, Swift ha campi immutabili, quindi devono essere inizializzati nella prima fase. E Swift applica queste regole di sicurezza.

    
risposta data 26.08.2016 - 10:15
fonte
2

Si consideri

  • Un metodo virtuale definito nella classe base può essere ridefinito nella classe derivata.
  • Il contraente della classe base può chiamare questo metodo virtuale direttamente o indirettamente.
  • Il metodo virtuale ridefinito (nella classe derivata) può dipendere dal valore di un campo nella classe derivata impostato correttamente nel responsabile della classe derivata.
  • L'appaltatore della classe derivata può chiamare un metodo nella classe base che dipende dai campi che sono stati impostati nel contraente della classe base.

Pertanto non esiste un design semplice che renda sicuri i contratti quando sono consentiti metodi virtuali , Swift evita questi problemi richiedendo l'inizializzazione in due fasi, offrendo quindi una maggiore sicurezza al programmatore, determinando allo stesso tempo un linguaggio più complesso.

Se puoi risolvere questi problemi in modo carino, non passare "Vai", passa direttamente alla raccolta del tuo PHD ...

    
risposta data 15.12.2016 - 12:42
fonte

Leggi altre domande sui tag