2014-06-14 12 views
22

Ho giocato con Swift e ho scoperto che quando eseguivo il casting di un oggetto da inserire in un dizionario, ricevo uno strano avviso: Treating a forced downcast to 'String' as optional will never produce 'nil'. Se sostituisco as con as?, l'avviso scompare.Trattare un downcast forzato come facoltativo non produrrà mai 'nil'

func test() -> AnyObject! { 
    return "Hi!" 
} 

var dict = Dictionary<String,String>() 
dict["test"]=test() as String' 

la documentazione di Apple dice il seguente

Perché downcasting può fallire, l'operatore di tipo fuso è disponibile in due forme diverse. Il modulo facoltativo, as?, restituisce un valore facoltativo del tipo a cui si sta tentando di eseguire il downcast. La forma forzata, as, tenta il downcast e forza il risultato come una singola azione composta.

Sono poco chiaro il motivo per cui utilizzando as? invece di as è corretto qui. Alcuni test rivelano che se cambio test() per restituire un Int al posto di una stringa, il codice si chiuderà con un errore se continuo a utilizzare as. Se si passa a utilizzare as?, il codice continuerà normalmente l'esecuzione e salterà tale istruzione (il dtt rimarrà vuoto). Tuttavia, non sono sicuro del motivo per cui è preferibile. A mio parere, preferirei che il programma si interrompesse con un errore e fammi sapere che il cast non ha avuto successo, quindi ignora semplicemente l'affermazione errata e continua a eseguire.

Secondo la documentazione, dovrei usare la forma forzata "solo quando sei sicuro che il downcast avrà sempre successo". In questo caso sono sicuro che il downcast avrà sempre successo poiché so che lo test() può restituire solo una stringa, quindi suppongo che questa sia una situazione perfetta per la forma forzata di down casting. Allora perché il compilatore mi sta dando un avvertimento?

+0

Prima di tutto, c'è un sacco di cacao tipo bridging in corso, perché senza 'importazione Foundation', non sarebbe nemmeno compilare, perché' STRING' non è un tipo di classe e non è compatibile con 'AnyObject'. Se cambi l'ultimo 'String' in' NSString', l'avviso scompare, il che significa probabilmente che si tratta di un problema con te che usa 'String', un tipo non di classe, in' as'. – newacct

+0

@newacct È interessante notare che se si sostituisce String con NSString nella definizione Dizionario o downcast, l'avviso scompare. Se si sostituisce String with NSString in ** both ** places, tuttavia, l'avviso rimane. – Pamelloes

+0

** NOTA **: Da Swift 2, molte delle funzionalità di 'as' sono state rimosse e sono state sostituite con' as! '. Così otterrai lo stesso errore quando usi 'as!'(usando' as' non genererà più questo errore) – Honey

risposta

35

Diamo uno sguardo più da vicino la vostra ultima linea, ed esplodere per vedere cosa sta succedendo:

let temporaryAnyObject = test() 
let temporaryString = temporaryAnyObject as String 
dict["test"] = temporaryString 

L'errore è sulla seconda riga, in cui si indica al compilatore di far rispettare che temporaryAnyObject è sicuramente un String. Il codice verrà comunque compilato (presupponendo che gli avvertimenti non vengano considerati come errori), ma si bloccherà se temporaryAnyObject non è in realtà un String.

Il modo as opere, senza ?, è fondamentalmente dicendo "da ora in poi, trattare il risultato di questa espressione come quel tipo se il risultato è in realtà di quel tipo, altrimenti siamo diventati incoerenti e non è più possibile eseguire .

il modo as? opere (con il ?) sta dicendo "da ora in poi, trattano il risultato di questa espressione come quel tipo se il risultato è in realtà di quel tipo, altrimenti il ​​risultato di questa espressione è nil.

Così, nel mio esempio esploso sopra, se test() non restituisce un String, poi gli afflitti as riesce, e temporaryString ora è un String. Senon restituisce un String, ma si dice un Int o qualsiasi altra cosa non sottoclasse da String, quindi as non riesce e il codice non può più continuare a funzionare.

Questo perché, poiché lo sviluppatore ha il controllo completo, hai detto al sistema di comportarsi in questo modo non inserendo l'indicatore opzionale ?.Il comando as significa in particolare che non si tollerano comportamenti opzionali e si richiede che il downcast funzioni.

Se tu avessi messo il ?, poi temporaryString sarebbe nil, e la terza linea sarebbe semplice rimuovere il "test" coppia chiave/valore dal dizionario.

Questo potrebbe sembrare strano, ma questo è solo perché questo è il comportamento di default contrario di molti linguaggi, come l'Obj-C, che trattano tutto come optional per impostazione predefinita e si basano su di voi per inserire le proprie verifiche e afferma.

Edit - Swift 2 Aggiornamento

Dal Swift 2, la forzata, l'operatore abbattuto failable as è stato rimosso, e viene sostituito con as!, che è molto Swiftier. Il comportamento è lo stesso.

+2

Capisco cosa stai dicendo su "as" vs "as?" ma ancora non capisco perché appare l'avvertimento. Inoltre, se ho diviso il mio codice in tre righe come hai delineato, l'avviso scompare. Si mostra solo quando l'affermazione avviene in una riga. – Pamelloes

+1

Ah, hai ragione. Beh, in realtà è perché non ero sbagliato nell'esplosione. Questo è effettivamente ciò che sarebbe: 'let temporaryAnyObject: AnyObject = test(); let temporaryString: String? = temporaryAnyObject as String; dict ["test"] = temporaryString' e come vedrai, che dà lo stesso avvertimento. – Ryan

+0

E il motivo è perché 'dict [" test "] = *' è in attesa di una stringa ?, quindi la modifica che ho appena postato digita esplicitamente come una stringa ?, non una stringa !. Pensa al "come" come a una funzione. La funzione "as" restituisce una stringa! mentre "come?" ritorna come stringa? – Ryan

1

È possibile risolvere questo avviso da due angoli. 1. Il valore restituito 2. Il tipo che si è previsto da restituire. L'altra risposta parla del 1 ° angolo. Sto parlando del 2 ° angolo di

Questo perché si restituisce un costretti scartato e pressofuso valore per una facoltativa. Il compilatore è come, "Se davvero si vuole forzare solo cast optionals allora perché non basta fare il parametro di rendimento atteso di essere un non facoltativa"

Per esempio, se hai scritto

func returnSomething<T> -> T?{ // I'm an optional, I can handle nils SAFELY and won't crash. 

return UIViewController as! T // will never return a safe nil, will just CRASH 

} 

Fondamentalmente hai detto a te stesso (e al compilatore) che voglio gestire in modo sicuro nil s ma poi nella riga successiva, hai detto nah, io no !!!

Il compilatore avrebbe dato un avvertimento:

Trattare una forzata abbattuto a 'T' come optional non potrà mai produrre 'nil'

Un'alternativa è quella di rimuovere il ?

func returnSomething<T>() -> T{ // I can't handle nils 

    return UIViewController() as! T // I will crash on nils 
} 

Detto questo, probabilmente il modo migliore è di non usare la forza cast e basta fare:

func returnSomething<T>() -> T?{ // I can handle nils 

    return UIViewController() as? T // I won't crash on nils 
} 
Problemi correlati