2014-12-23 14 views
31

L'intero motivo degli optionals è di impedire arresti anomali del runtime risultanti dal colpire una variabile assegnata a nil/null/none. Pertanto, le variabili non possono essere nul; possono invece essere racchiusi in un tipo facoltativo che li rappresenta come Alcuni o Nessuno e scartati per ottenere il contenuto specifico di Alcuni o zero.In Swift, come posso evitare sia gli optionals che i riferimenti agli oggetti nil?

Ma se li scarti dappertutto con !, o con Optionals Implicitamente Unwrapped, hai solo la possibilità, dal momento che sei un programmatore imperfetto, di crash in fase di esecuzione. Se si utilizza if let per scartarli in modo sicuro, si evitano arresti anomali, ma si è bloccati all'interno dell'ambito dell'istruzione if let per lavorare con il valore all'interno di Alcuni e si deve comunque gestire il potenziale caso nullo. Se usi? per scartare temporaneamente per chiamare un metodo, verrà riavvolto al termine, introducendo la possibilità disordinata di un wrapping facoltativo a più livelli.

Quindi: mi sembra che la cosa da fare sia evitare gli optionals tranne quando necessario (ad es. Quando si chiamano metodi framework che li restituiscono). Ma se non sto usando gli optionals, significa che i miei riferimenti agli oggetti devono essere non nulli, e non riesco a capire come gestire i casi in cui, per una ragione o l'altra, non ci dovrebbe essere o non lo è , un valore assegnato a un riferimento a un oggetto.

La mia domanda è: come evitare il nil? Sembra che richieda un approccio di programmazione diverso. (Oppure dovrei semplicemente usare gli optionals, e se è quello che sto facendo, come è meglio avere semplicemente assegnazioni nulle ai riferimenti a oggetti come hanno altri linguaggi?)

So che questa è potenzialmente una domanda soggettiva , ma dove altro dovrei chiederlo? Non sto cercando di trollare o suscitare dibattiti, mi piacerebbe davvero sapere qual è l'approccio corretto mentre scrivo più codice Swift.

+6

Hai mai sentito parlare di programmazione orientata alle ferrovie? Ecco un post che spiega questo concetto in F # http://fsharpforfunandprofit.com/posts/recipe-part2/ – mustafa

+0

ottima domanda, guarda in Programmazione funzionale e (eventualmente) Funzionale Swift – Nick

+6

Se stai per lanciare l'ultimo voto per chiudere questa domanda è troppo soggettiva, potresti commentare cosa c'è di sbagliato in questo? La domanda è davvero un come-faccio-io-faccio-questo, e la risposta in realtà risponde. Se pensi davvero che dovrebbe essere chiuso, cosa avrei potuto fare per migliorarlo? –

risposta

83

Hai ragione, gli optionals possono essere un dolore, motivo per cui non dovresti esagerare. Ma sono più di qualcosa che devi affrontare quando usi i framework.Sono una soluzione a un problema incredibilmente comune: come gestire una chiamata che restituisce un risultato che potrebbe essere OK o meno.

Ad esempio, prendere il membro Array.first. Questa è una pratica utility che ti dà il primo elemento di un array. Perché è utile essere in grado di chiamare a.first, quando si può semplicemente chiamare a[0]? Perché a runtime la matrice potrebbe essere vuota, nel qual caso esploderà a[0]. Naturalmente è possibile controllare in anticipo il a.count -

a. si potrebbe dimenticare,

e

b. questo si traduce in un codice abbastanza brutto.

Array.first si occupa di ciò restituendo un facoltativo. Quindi sei costretto a scartare l'opzionale prima di poter usare il valore che è il primo elemento dell'array.

Ora, per il problema relativo al valore scartato solo esistente all'interno del blocco con if let. Immagina il codice parallelo, controllando il numero di array. Sarebbe lo stesso, vero?

if a.count > 0 { 
    // use a[0] 
} 
// outside the block, no guarantee 
// a[0] is valid 

if let firstElement = a.first { 
    // use firstElement 
} 
// outside the block, you _can't_ 
// use firstElement 

Certo, si potrebbe fare qualcosa come eseguire un ritorno anticipato dalla funzione se il conteggio è zero. Funziona ma è un po 'a rischio - cosa succede se ti sei dimenticato di farlo, o di metterlo in una dichiarazione condizionale che non è stata eseguita? In sostanza, puoi fare lo stesso con array.first: controlla il conteggio all'inizio della funzione, quindi in seguito fai array.first!. Ma quello ! è come un segnale per te - stai attento, stai facendo qualcosa di pericoloso e sarai dispiaciuto se il tuo codice non è completamente corretto.

Gli optionals aiutano anche a rendere le alternative leggermente più belle. Supponiamo di volere il valore predefinito se la matrice è vuota. Invece di questo:

array.count > 0 ? a[0] : somedefault 

si può scrivere questo:

array.first ?? somedefault 

Questo è più bello in diversi modi. Mette la cosa importante in primo piano: il valore che vuoi è come inizia l'espressione, seguito dal default. A differenza dell'espressione ternaria, che ti colpisce prima con l'espressione di controllo, seguita dal valore che effettivamente desideri, e infine il valore predefinito. È anche più infallibile: è molto più facile evitare errori di battitura, e impossibile che l'errore di battitura porti a un'esplosione di runtime.

Prendete un altro esempio: la funzione find. Questo controlla se un valore è in una collezione e restituisce l'indice della sua posizione. Ma il valore potrebbe non essere presente nella collezione. Altre lingue potrebbero gestirlo restituendo l'indice finale (che non punta a un valore ma piuttosto a uno passato rispetto all'ultimo valore). Questo è ciò che viene definito un valore "sentinella": un valore che assomiglia a un risultato regolare, ma che in realtà ha un significato speciale. Come nell'esempio precedente, dovresti controllare che il risultato non sia uguale all'indice finale prima di usarlo. Ma per fare questo devi conoscere. Devi cercare i documenti per find e confermare come funziona.

Con find restituire un optional è naturale, quando si capisce l'idioma opzionale, rendersi conto che il motivo è che il risultato potrebbe non essere valido per l'ovvia ragione.E si applicano anche le stesse cose sulla sicurezza di cui sopra - non puoi dimenticartene accidentalmente, e usa il risultato come indice, perché devi prima scartarlo.

Detto questo, è possibile sovrautilizzare gli optionals in quanto sono un onere da controllare. Questo è il motivo per cui gli indici di array non restituiscono gli optionals: creerebbe così tanto fastidio da doverli controllare e scartare continuamente, specialmente quando si sa che l'indice che si sta usando è valido (ad esempio, sei in un ciclo for su un intervallo di indice valido dell'array) che le persone utilizzerebbero costantemente ! e quindi ingombrano il loro codice senza trarne vantaggio. Ma poi i metodi di supporto come il primo e l'ultimo vengono aggiunti per coprire casi comuni in cui le persone desiderano eseguire rapidamente un'operazione senza dover prima controllare la dimensione dell'array, ma vogliono farlo in modo sicuro.

(Swift dizionari, d'altra parte, si prevede di accedere regolarmente tramite indici non validi, che è il motivo per cui il loro metodo [key] fa ritorno opzionale)

Ancora meglio è se la possibilità di fallimento può essere evitato del tutto . Ad esempio, quando filter non corrisponde a nessun elemento, non viene restituito un valore nil opzionale. Restituisce un array vuoto. "Beh, ovviamente lo sarebbe", si potrebbe dire. Ma ti sorprenderebbe quanto spesso vedi qualcuno fare un valore di ritorno come un array opzionale quando in realtà dovrebbero solo restituire uno vuoto. Quindi hai perfettamente ragione nel dire che dovresti evitare gli optionals, tranne quando sono necessari - è solo una questione di quali mezzi necessari. Negli esempi sopra, direi che sono necessari e una soluzione migliore per le alternative.

+11

Questa è una risposta davvero ponderata e approfondita, e apprezzo il fatto che abbiate preso il tempo di spiegarlo con casi d'uso reali su quando o quando gli optionals non hanno senso. Grazie! –

+4

Cose fantastiche. Probabilmente dovresti scrivere un blog veloce o qualcosa del genere;) – jrturton

+0

Scrive già [uno] (https://airspeedvelocity.net/). – Zaxter

13

O dovrei usare solo optional, e se questo è quello che sto facendo, come è meglio di avere semplicemente le assegnazioni nulli di opporsi riferimenti come le altre lingue hanno?

Se il calcolo potrebbe dover restituire un valore "speciale", allora sì, in Swift si suppone di utilizzare gli optionals. Sono migliori dei tipi nullable perché sono espliciti. È facile perdere un caso in cui un puntatore potrebbe essere nil, è molto più difficile (ma del tutto possibile) rovinare gli optionals.

Se si utilizza “se lasciate” a loro scartare in modo sicuro, a evitare crash, ma si sono bloccati all'interno del campo di applicazione del “se lasciare che” l'istruzione a lavorare con il valore , e avete ancora da gestire il potenziale caso zero.

Questa è una funzionalità. Voglio dire, questo è il punto di un tipo facoltativo: devi gestire entrambi i casi (nil e non- nil) e devi essere esplicito a riguardo.

Vedere Haskell's Maybe type and monad per un esempio di un concetto simile. Il tipo Maybe è l'equivalente esatto dei tipi opzionali, la monade Maybe rende molto facile "operare a catena" con questi valori opzionali senza dover controllare manualmente i valori vuoti per tutto il tempo.

+1

Ciò è utile nella distinzione tra un essere facoltativo esplicito e un riferimento null che non lo è. Grazie. –

+0

Puoi commentare la frase "* Se si utilizza? Per scartare temporaneamente per chiamare un metodo, verrà riavvolto al termine, introducendo la possibilità disordinata di un involucro facoltativo a più strati. *"? Gli opzionali rapidi non sono monadici, ma si comportano solo come i funtori? – Bergi

+0

@Bergi, in Objective-C, puoi inviare un messaggio a un oggetto 'nil' e non succede niente di male. Il concatenamento opzionale in Swift è solo una versione generalizzata di questa funzione. AFAIK non puoi usarlo per qualcosa di più complesso dell'accesso di proprietà nidificate, chiamate di metodi concatenate e cose del genere. (Non sono sicuro se questo risponda alla tua domanda su monadi e funtori, sfortunatamente non ne so molto sulla programmazione funzionale.) – zoul

Problemi correlati