Che cos'è un "valore non scartato" in Swift?


155

Sto imparando Swift per iOS 8 / OSX 10.10 seguendo questo tutorial e il termine " valore non imballato " viene usato più volte, come in questo paragrafo (in Oggetti e classe ):

Quando lavori con valori opzionali, puoi scrivere? prima di operazioni come metodi, proprietà e sottoscrizione. Se il valore prima del? è zero, tutto dopo il? viene ignorato e il valore dell'intera espressione è zero. Altrimenti, il valore facoltativo non viene scartato e tutto dopo il? agisce sul valore da scartare . In entrambi i casi, il valore dell'intera espressione è un valore facoltativo.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare?.sideLength

Non capisco e ho cercato sul web senza fortuna.

Cosa significa questo?


modificare

Dalla risposta di Cezary, c'è una leggera differenza tra l'output del codice originale e la soluzione finale (testata su un parco giochi):

Codice originale

Codice originale

La soluzione di Cezary

La soluzione di Cezary

Le proprietà della superclasse sono mostrate nell'output nel secondo caso, mentre nel primo caso c'è un oggetto vuoto.

Il risultato non dovrebbe essere identico in entrambi i casi?

Domande e risposte correlate: qual è un valore facoltativo in Swift?


1
La risposta di Cezary è perfetta. Inoltre, c'è un grande libro introduttivo su Swift disponibile GRATUITAMENTE su iBooks
Daniel Storm,

@DanielStormApps Questo iBook è una versione portatile del tutorial collegato nella mia domanda :)
Bigood,

Risposte:


289

Innanzitutto, devi capire cos'è un tipo opzionale. Un tipo facoltativo significa sostanzialmente che la variabile può essere nil .

Esempio:

var canBeNil : Int? = 4
canBeNil = nil

Il punto interrogativo indica il fatto che canBeNilpuò essere nil.

Questo non funzionerebbe:

var cantBeNil : Int = 4
cantBeNil = nil // can't do this

Per ottenere il valore dalla variabile se è facoltativo, è necessario scartarlo . Questo significa solo mettere un punto esclamativo alla fine.

var canBeNil : Int? = 4
println(canBeNil!)

Il tuo codice dovrebbe apparire così:

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare!.sideLength

Un sidenote:

Puoi anche dichiarare gli opzionali da scartare automaticamente usando un punto esclamativo anziché un punto interrogativo.

Esempio:

var canBeNil : Int! = 4
print(canBeNil) // no unwrapping needed

Quindi un modo alternativo per correggere il tuo codice è:

let optionalSquare: Square! = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare.sideLength

MODIFICARE:

La differenza che stai vedendo è esattamente il sintomo del fatto che il valore opzionale è racchiuso . C'è un altro livello sopra di esso. La versione da scartare mostra semplicemente l'oggetto diritto perché è, beh, da scartare.

Un rapido confronto sul parco giochi:

terreno di gioco

Nel primo e nel secondo caso, l'oggetto non viene automaticamente scartato, quindi vengono visualizzati due "livelli" ( {{...}}), mentre nel terzo caso viene visualizzato solo un livello ( {...}) perché l'oggetto viene automaticamente scartato.

La differenza tra il primo e il secondo caso è che il secondo caso ti darà un errore di runtime se optionalSquareimpostato su nil. Utilizzando la sintassi nel primo caso, puoi fare qualcosa del genere:

if let sideLength = optionalSquare?.sideLength {
    println("sideLength is not nil")
} else {
    println("sidelength is nil")
}

1
Grazie per la chiara spiegazione! Il codice incluso nella mia domanda è citato dal tutorial di Apple (link nella domanda), e suppongo che dovrebbe funzionare così com'è (e lo fa). Tuttavia, la tua soluzione finale e quella originale producono risultati diversi (vedi la mia modifica). Qualche pensiero?
Bigood,

2
Freddo. Ottima spiegazione Sono ancora confuso, comunque. Perché dovrei usare un valore opzionale? Quando è utile? Perché Apple ha creato questo comportamento e sintassi?
Todd Lehman,

1
È particolarmente rilevante quando si tratta di proprietà di classe. Swift richiede che tutti i metodi non opzionali siano inizializzati nella init()funzione della classe. Consiglio di leggere i capitoli "Le basi" (copre gli opzionali), "Inizializzazione" e "Opzionale" nel libro Swift pubblicato da Apple.
Cezary Wojcik,

2
@ToddLehman Apple ha creato questo per aiutarti a scrivere codice molto più sicuro. Ad esempio, immagina tutte le eccezioni del puntatore null in Java che bloccano un programma perché qualcuno ha dimenticato di verificare la presenza di null. O un comportamento strano su Objective-C perché hai dimenticato di controllare lo zero. Con il tipo opzionale non puoi dimenticare di occuparti di zero. Il compilatore ti costringe a gestirlo.
Erik Engheim,

5
@AdamSmith - Provenendo da uno sfondo Java, posso sicuramente vedere l'uso degli optionals ... ma anche venendo (più recentemente) da uno sfondo Objective-C, ho ancora problemi a vederne l'uso, perché Objective- C consente esplicitamente di inviare messaggi a zero oggetti. Hmm. Mi piacerebbe vedere qualcuno scrivere un esempio di questi che mostrano come aiutano a rendere il codice più breve e sicuro rispetto al codice equivalente che non li utilizza. Non sto dicendo che non siano utili; Sto solo dicendo che non ho ancora "capito".
Todd Lehman,

17

La risposta corretta esistente è ottima, ma ho scoperto che per comprenderlo appieno, avevo bisogno di una buona analogia, poiché si tratta di un concetto molto astratto e strano.

Consentitemi quindi di aiutare gli altri sviluppatori "mentalmente" (pensiero visivo) a dare una prospettiva diversa oltre alla risposta corretta. Ecco una buona analogia che mi ha aiutato molto.

Analogia avvolgente del regalo di compleanno

Pensa agli opzionali come a regali di compleanno che si presentano in una confezione rigida, dura e colorata.

Non sai se c'è qualcosa dentro la confezione fino a quando non scarterai il presente - forse non c'è niente dentro! Se dentro c'è qualcosa, potrebbe trattarsi di un altro regalo, anch'esso avvolto e che potrebbe anche non contenere nulla . Potresti anche scartare 100 regali nidificati per scoprire finalmente che non c'era nient'altro che avvolgimento .

Se il valore dell'opzionale non lo è nil, ora hai rivelato una casella contenente qualcosa . Ma, soprattutto se il valore non viene digitato in modo esplicito ed è una variabile e non una costante predefinita, potrebbe essere necessario aprire la casella prima di poter sapere qualcosa di specifico su ciò che è nella casella, come che tipo è, o quale il valore reale è.

Cosa c'è nella scatola?! Analogia

Anche dopo aver scartato la variabile, sei ancora come Brad Pitt nell'ultima scena di SE7EN ( avvertimento : spoiler e linguaggio osceno e violenza molto votati a R), perché anche dopo aver scartato il presente, ti trovi nella seguente situazione: ora hai nil, o una scatola contenente qualcosa (ma non sai cosa).

Potresti conoscere il tipo di qualcosa . Ad esempio, se hai dichiarato la variabile come tipo, [Int:Any?]allora sapresti di avere un dizionario (potenzialmente vuoto) con sottoscrizioni di numeri interi che restituisce contenuti di qualsiasi tipo precedente.

Ecco perché trattare con i tipi di raccolta (dizionari e array) in Swift può diventare un po 'peloso.

Caso in questione:

typealias presentType = [Int:Any?]

func wrap(i:Int, gift:Any?) -> presentType? {
    if(i != 0) {
        let box : presentType = [i:wrap(i-1,gift:gift)]
        return box
    }
    else {
        let box = [i:gift]
        return box
    }
}

func getGift() -> String? {
    return "foobar"
}

let f00 = wrap(10,gift:getGift())

//Now we have to unwrap f00, unwrap its entry, then force cast it into the type we hope it is, and then repeat this in nested fashion until we get to the final value.

var b4r = (((((((((((f00![10]! as! [Int:Any?])[9]! as! [Int:Any?])[8]! as! [Int:Any?])[7]! as! [Int:Any?])[6]! as! [Int:Any?])[5]! as! [Int:Any?])[4]! as! [Int:Any?])[3]! as! [Int:Any?])[2]! as! [Int:Any?])[1]! as! [Int:Any?])[0])

//Now we have to DOUBLE UNWRAP the final value (because, remember, getGift returns an optional) AND force cast it to the type we hope it is

let asdf : String = b4r!! as! String

print(asdf)

bello 😊 😊😊 😊 😊😊 😊 😊😊
SamSol

amo l'analogia! quindi, c'è un modo migliore per scartare le scatole? nel tuo esempio di codice
David T.

Il gatto di Schrödinger è nella scatola
Jacksonkr,

Grazie per avermi confuso .... Che tipo di programmatore dà un regalo di compleanno che è comunque vuoto? kinda mean
delta2flat

@Jacksonkr O no.
amsmath,

13

Swift attribuisce un valore elevato alla sicurezza del tipo. L'intero linguaggio Swift è stato progettato pensando alla sicurezza. È uno dei tratti distintivi di Swift e uno che dovresti dare il benvenuto a braccia aperte. Contribuirà allo sviluppo di codice pulito e leggibile e contribuirà a impedire il crash dell'applicazione.

Tutti gli optionals in Swift sono delimitati dal ?simbolo. Impostando ?dopo il nome del tipo in cui si dichiara come facoltativo, essenzialmente si esegue il cast di questo non come tipo in cui si trova prima di ?, ma invece come tipo facoltativo .


Nota: una variabile o un tipo nonInt è uguale a . Sono due tipi diversi che non possono essere gestiti l'uno sull'altro.Int?


Usando Opzionale

var myString: String?

myString = "foobar"

Questo non significa che stai lavorando con un tipo di String. Ciò significa che stai lavorando con un tipo di String?(String opzionale o String opzionale). In effetti, ogni volta che provi a farlo

print(myString)

in fase di esecuzione, verrà stampata la console di debug Optional("foobar"). La parte " Optional()" indica che questa variabile può avere o meno un valore in fase di runtime, ma al momento accade che attualmente contenga la stringa "foobar". Questa " Optional()" indicazione rimarrà a meno che tu non faccia quello che viene chiamato "scartare" il valore opzionale.

Scartare un facoltativo significa che stai lanciando quel tipo come non opzionale. Ciò genererà un nuovo tipo e assegnerà il valore che risiedeva in quello opzionale al nuovo tipo non opzionale. In questo modo è possibile eseguire operazioni su quella variabile in quanto è stato garantito dal compilatore un valore solido.


Unwrapping condizionale verificherà se il valore nell'opzionale è nilo meno. In caso contrario nil, ci sarà una variabile di costante appena creata a cui verrà assegnato il valore e che verrà scartato nella costante non opzionale. E da lì puoi tranquillamente utilizzare il non opzionale nel ifblocco.

Nota: è possibile assegnare alla costante non imballata condizionale lo stesso nome della variabile facoltativa che si sta scartando.

if let myString = myString {
    print(myString)
    // will print "foobar"
}

Gli optionals da scartare condizionatamente sono il modo più pulito per accedere al valore di un opzionale perché se contiene un valore nullo, tutto ciò che si trova all'interno del blocco if let non verrà eseguito. Naturalmente, come ogni istruzione if, puoi includere un blocco else

if let myString = myString {
    print(myString)
    // will print "foobar"
}
else {
    print("No value")
}

Il Unwrapping forzato viene eseguito impiegando quello che è noto come !operatore ("bang"). Questo è meno sicuro ma consente comunque la compilazione del codice. Tuttavia, ogni volta che usi l'operatore bang devi essere sicuro al 1000% che la tua variabile contenga effettivamente un valore solido prima di scartarla con la forza.

var myString: String?

myString = "foobar"

print(myString!)

Questo sopra è un codice Swift completamente valido. Stampa il valore di myStringquello impostato come "foobar". L'utente vedrebbe foobarstampato nella console e questo è tutto. Ma supponiamo che il valore non sia mai stato impostato:

var myString: String?

print(myString!) 

Ora abbiamo una situazione diversa nelle nostre mani. A differenza di Objective-C, ogni volta che si tenta di scartare forzatamente un facoltativo, e l'opzione non è stata impostata ed è nil, quando si tenta di scartare l'opzionale per vedere cosa c'è dentro l'applicazione si bloccherà.


Involucro con tipo Casting . Come abbiamo detto in precedenza che, mentre sei unwrappingfacoltativo, stai effettivamente trasmettendo a un tipo non opzionale, puoi anche trasmettere il non opzionale a un tipo diverso. Per esempio:

var something: Any?

Da qualche parte nel nostro codice la variabile somethingverrà impostata con un certo valore. Forse stiamo usando generici o forse c'è qualche altra logica in corso che lo farà cambiare. Quindi più avanti nel nostro codice vogliamo usare, somethingma essere comunque in grado di trattarlo in modo diverso se si tratta di un tipo diverso. In questo caso, dovrai utilizzare la asparola chiave per determinare questo:

Nota: l' asoperatore spiega come digitare cast in Swift.

// Conditionally
if let thing = something as? Int {
    print(thing) // 0
}

// Optionally
let thing = something as? Int
print(thing) // Optional(0)

// Forcibly
let thing = something as! Int
print(thing) // 0, if no exception is raised

Notare la differenza tra le due asparole chiave. Come prima quando abbiamo scartato con la forza un optional, abbiamo usato l' !operatore bang per farlo. Qui farai la stessa cosa ma invece di lanciare solo un non opzionale, lo stai lanciando anche come Int. E deve essere in grado di essere downcast come Int, altrimenti, come usare l'operatore bang quando il valore è che la niltua applicazione andrà in crash.

E per usare queste variabili in qualche modo o operazione matematica, devono essere scartate per poterlo fare.

Ad esempio, in Swift possono essere gestiti solo tipi di dati numerici validi dello stesso tipo. Quando lanci un tipo con il as!tuo, stai forzando il downcast di quella variabile come se tu fossi certo che sia di quel tipo, quindi sicuro da usare e non bloccare la tua applicazione. Questo va bene fintanto che la variabile è davvero del tipo in cui la stai lanciando, altrimenti avrai un pasticcio tra le mani.

Tuttavia, il casting con as!consentirà la compilazione del codice. Casting con an as?è una storia diversa. In effetti, as?dichiara il tuo Intcome un tipo di dati completamente diverso tutti insieme.

Ora è Optional(0)

E se hai mai provato a fare i compiti scrivendo qualcosa del genere

1 + Optional(1) = 2

Il tuo insegnante di matematica ti avrebbe probabilmente dato una "F". Lo stesso con Swift. Tranne che Swift preferirebbe non compilare affatto piuttosto che darti un voto. Perché alla fine della giornata, infatti, l'opzione potrebbe essere nulla .

Safety First Kids.


Bella spiegazione dei tipi opzionali. Mi ha aiutato a chiarire le cose.
Tobe_Sta,

0

'?' significa espressione concatenata facoltativa che

il '!' significa forza-valore
inserisci qui la descrizione dell'immagine

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.