Che cosa significa "Errore irreversibile: trovato inaspettatamente nullo durante lo scartamento di un valore opzionale"?
Il mio programma Swift si blocca con EXC_BAD_INSTRUCTION
e uno dei seguenti errori simili. Cosa significa questo errore e come risolverlo?
Errore irreversibile: inaspettatamente trovato zero durante lo scartamento di un valore facoltativo
o
Errore irreversibile: inaspettatamente trovato zero durante lo scartamento implicito di un valore facoltativo
Questo post ha lo scopo di raccogliere risposte a problemi "trovati inaspettatamente zero", in modo che non siano sparsi e difficili da trovare. Sentiti libero di aggiungere la tua risposta o modificare la risposta wiki esistente.
Risposte
Questa risposta è wiki della comunità . Se ritieni che potrebbe essere migliorato, sentiti libero di modificarlo !
Background: cos'è un optional?
In Swift, Optional<Wrapped>
è un tipo di opzione : può contenere qualsiasi valore dal tipo originale ("Wrapped") o nessun valore (il valore speciale nil
). Un valore facoltativo deve essere scartato prima di poter essere utilizzato.
Facoltativo è un tipo generico , il che significa che Optional<Int>
e Optional<String>
sono tipi distinti: il tipo all'interno <>
è chiamato tipo Wrapped. Sotto il cofano, un Optional è un enum con due casi: .some(Wrapped)
e .none
, dove .none
è equivalente a nil
.
Gli optionals possono essere dichiarati utilizzando il tipo denominato Optional<T>
o (più comunemente) come abbreviazione con un ?
suffisso.
var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int? // `nil` is the default when no value is provided
var aVerboseOptionalInt: Optional<Int> // equivalent to `Int?`
anOptionalInt = nil // now this variable contains nil instead of an integer
Gli optionals sono uno strumento semplice ma potente per esprimere i tuoi presupposti durante la scrittura del codice. Il compilatore può utilizzare queste informazioni per impedirti di commettere errori. Dal linguaggio di programmazione Swift :
Swift è un type-safe lingua, il che significa la lingua vi aiuta ad essere chiari circa i tipi di valori il codice può lavorare. Se una parte del codice richiede un
String
, l'indipendenza dai tipi ti impedisce di passarloInt
per errore. Allo stesso modo, l'indipendenza dai tipi impedisce di passare accidentalmente un facoltativoString
a una parte di codice che richiede un non facoltativoString
. La sicurezza dei tipi ti aiuta a rilevare e correggere gli errori il prima possibile nel processo di sviluppo.
Alcuni altri linguaggi di programmazione hanno anche tipi di opzioni generiche : ad esempio, Forse in Haskell, opzione in Rust e facoltativa in C ++ 17.
Nei linguaggi di programmazione senza tipi di opzione, un particolare valore "sentinella" viene spesso utilizzato per indicare l'assenza di un valore valido. In Objective-C, ad esempio, nil
(il puntatore nullo ) rappresenta la mancanza di un oggetto. Per tipi primitivi come int
, non è possibile utilizzare un puntatore nullo, quindi è necessaria una variabile separata (come value: Int
e isValid: Bool
) o un valore sentinella designato (come -1
o INT_MIN
). Questi approcci sono soggetti a errori perché è facile dimenticare di controllare isValid
o controllare il valore sentinella. Inoltre, se un valore particolare viene scelto come sentinella, significa che non può più essere considerato un valore valido .
I tipi di opzione come Swift Optional
risolvono questi problemi introducendo un nil
valore speciale e separato (quindi non è necessario designare un valore sentinel) e sfruttando il sistema di tipi forti in modo che il compilatore possa aiutarti a ricordare di verificare la presenza di zero quando necessario.
Perché ho ricevuto " errore irreversibile: inaspettatamente trovato zero durante lo scartamento di un valore opzionale "?
Per accedere al valore di un optional (se ne ha uno), è necessario scartarlo . Un valore opzionale può essere scartato in modo sicuro o forzato. Se esegui lo scartamento forzato di un facoltativo e non aveva un valore, il tuo programma andrà in crash con il messaggio precedente.
Xcode ti mostrerà l'arresto anomalo evidenziando una riga di codice. Il problema si verifica su questa riga.
Questo arresto anomalo può verificarsi con due diversi tipi di chiusura forzata:
1. Disimballaggio forzato esplicito
Questo viene fatto con l' !
operatore su un optional. Per esempio:
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
Errore irreversibile: inaspettatamente trovato zero durante lo scartamento di un valore facoltativo
Come anOptionalString
è nil
qui, si otterrà un incidente sulla linea in cui si forza scartarlo.
2. Optionals implicitamente non confezionate
Questi sono definiti con una !
, piuttosto che ?
dopo il tipo.
var optionalDouble: Double! // this value is implicitly unwrapped wherever it's used
Si presume che questi optionals contengano un valore. Pertanto, ogni volta che accedi a un optional implicitamente scartato, questo verrà automaticamente scartato per te. Se non contiene un valore, andrà in crash.
print(optionalDouble) // <- CRASH
Errore irreversibile: inaspettatamente trovato zero durante lo scartamento implicito di un valore facoltativo
Per capire quale variabile ha causato l'arresto anomalo, puoi tenere premuto ⌥mentre fai clic per mostrare la definizione, dove potresti trovare il tipo opzionale.
Gli IBOutlet, in particolare, sono di solito optionals implicitamente scartati. Questo perché il tuo xib o storyboard collegherà i punti vendita in fase di esecuzione, dopo l' inizializzazione. Dovresti quindi assicurarti di non accedere alle prese prima che vengano caricate. Dovresti anche controllare che le connessioni siano corrette nel tuo file storyboard / xib, altrimenti i valori saranno nil
in fase di runtime, e quindi andranno in crash quando sono implicitamente scartati . Quando si riparano le connessioni, provare a eliminare le righe di codice che definiscono le prese, quindi ricollegarle.
Quando dovrei mai forzare a scartare un optional?
Disimballaggio forzato esplicito
Come regola generale, non dovresti mai forzare esplicitamente a scartare un optional con l' !
operatore. Potrebbero esserci casi in cui l'uso !
è accettabile, ma dovresti usarlo solo se sei sicuro al 100% che l'opzionale contiene un valore.
Mentre ci può essere un'occasione in cui è possibile usare la forza scartare, come si sa per un fatto che un optional contiene un valore - non c'è un singolo posto in cui non si può tranquillamente scartare quella facoltativa, invece.
Optionals implicitamente non confezionate
Queste variabili sono progettate in modo da poter rinviare la loro assegnazione a un momento successivo nel codice. È tua responsabilità assicurarti che abbiano un valore prima di accedervi. Tuttavia, poiché implicano lo scartamento forzato, sono ancora intrinsecamente non sicuri, poiché presumono che il tuo valore non sia nullo, anche se l'assegnazione di nil è valida.
Dovresti usare solo optionals implicitamente scartati come ultima risorsa . Se puoi usare una variabile lazy o fornire un valore predefinito per una variabile, dovresti farlo invece di usare un facoltativo implicitamente scartato.
Tuttavia, ci sono alcuni scenari in cui gli optional implicitamente scartati sono vantaggiosi e sei ancora in grado di utilizzare vari modi per scartarli in modo sicuro come elencato di seguito, ma dovresti sempre usarli con la dovuta cautela.
Come posso trattare in sicurezza con gli Optionals?
Il modo più semplice per verificare se un facoltativo contiene un valore è confrontarlo con nil
.
if anOptionalInt != nil {
print("Contains a value!")
} else {
print("Doesn’t contain a value.")
}
Tuttavia, il 99,9% delle volte quando si lavora con gli optional, si vorrà effettivamente accedere al valore che contiene, se ne contiene uno. A tale scopo, è possibile utilizzare l' associazione facoltativa .
Rilegatura opzionale
L'associazione facoltativa consente di verificare se un facoltativo contiene un valore e consente di assegnare il valore da scartare a una nuova variabile o costante. Utilizza la sintassi if let x = anOptional {...}
o if var x = anOptional {...}
, a seconda se è necessario modificare il valore della nuova variabile dopo averla associata.
Per esempio:
if let number = anOptionalInt {
print("Contains a value! It is \(number)!")
} else {
print("Doesn’t contain a number")
}
Ciò che fa è innanzitutto verificare che l'opzionale contenga un valore. In caso affermativo , il valore "scartato" viene assegnato a una nuova variabile ( number
), che puoi utilizzare liberamente come se non fosse opzionale. Se l'opzionale non contiene un valore, verrà richiamata la clausola else, come ci si aspetterebbe.
La cosa bella della rilegatura opzionale è che puoi scartare più opzioni allo stesso tempo. Puoi semplicemente separare le istruzioni con una virgola. L'istruzione avrà esito positivo se tutti gli optional fossero stati scartati.
var anOptionalInt : Int?
var anOptionalString : String?
if let number = anOptionalInt, let text = anOptionalString {
print("anOptionalInt contains a value: \(number). And so does anOptionalString, it’s: \(text)")
} else {
print("One or more of the optionals don’t contain a value")
}
Un altro trucco interessante è che puoi anche usare le virgole per verificare una certa condizione sul valore, dopo averlo scartato.
if let number = anOptionalInt, number > 0 {
print("anOptionalInt contains a value: \(number), and it’s greater than zero!")
}
L'unico problema con l'utilizzo di un'associazione facoltativa all'interno di un'istruzione if è che è possibile accedere al valore scartato solo dall'ambito dell'istruzione. Se è necessario accedere al valore dall'esterno dell'ambito dell'istruzione, è possibile utilizzare un'istruzione guard .
Un'istruzione guard consente di definire una condizione per il successo e l'ambito corrente continuerà l'esecuzione solo se tale condizione viene soddisfatta. Sono definiti con la sintassi guard condition else {...}
.
Quindi, per usarli con un'associazione opzionale, puoi fare questo:
guard let number = anOptionalInt else {
return
}
(Si noti che all'interno del corpo di guardia, è necessario utilizzare una delle istruzioni di trasferimento del controllo per uscire dall'ambito del codice attualmente in esecuzione).
Se anOptionalInt
contiene un valore, verrà scartato e assegnato alla nuova number
costante. Il codice dopo la guardia continuerà quindi l'esecuzione. Se non contiene un valore, la guardia eseguirà il codice tra parentesi, il che porterà al trasferimento del controllo, in modo che il codice immediatamente successivo non venga eseguito.
La vera cosa bella delle istruzioni guard è che il valore scartato è ora disponibile per l'uso nel codice che segue l'istruzione (come sappiamo che il codice futuro può essere eseguito solo se l'opzionale ha un valore). Questo è ottimo per eliminare le "piramidi del destino" create annidando più istruzioni if.
Per esempio:
guard let number = anOptionalInt else {
return
}
print("anOptionalInt contains a value, and it’s: \(number)!")
Le protezioni supportano anche gli stessi trucchi accurati supportati dall'istruzione if, come scartare più optionals contemporaneamente e usare la where
clausola.
L'utilizzo di un'istruzione if o guard dipende completamente dal fatto che qualsiasi codice futuro richieda che l'opzionale contenga un valore.
Nil Coalescing Operator
L' operatore Nil Coalescing è un'elegante versione abbreviata dell'operatore condizionale ternario , progettato principalmente per convertire gli optional in non-optionals. Ha la sintassi a ?? b
, dove a
è un tipo facoltativo ed b
è lo stesso tipo di a
(sebbene di solito non facoltativo).
In sostanza, ti consente di dire "Se a
contiene un valore, scartalo. Se non lo fa, torna b
invece ”. Ad esempio, potresti usarlo in questo modo:
let number = anOptionalInt ?? 0
Questo definirà una number
costante di Int
tipo, che conterrà il valore di anOptionalInt
, se contiene un valore, o 0
altrimenti.
È solo una scorciatoia per:
let number = anOptionalInt != nil ? anOptionalInt! : 0
Concatenamento opzionale
È possibile utilizzare il concatenamento facoltativo per chiamare un metodo o accedere a una proprietà su un file facoltativo. Questo viene fatto semplicemente aggiungendo un suffisso al nome della variabile ?
quando la si utilizza.
Ad esempio, supponiamo di avere una variabile foo
, di tipo Foo
un'istanza facoltativa .
var foo : Foo?
Se volessimo chiamare un metodo foo
che non restituisce nulla, possiamo semplicemente fare:
foo?.doSomethingInteresting()
Se foo
contiene un valore, questo metodo verrà chiamato su di esso. In caso contrario, non accadrà nulla di male: il codice continuerà semplicemente l'esecuzione.
(Questo è un comportamento simile all'invio di messaggi a nil
in Objective-C)
Questo può quindi essere utilizzato anche per impostare proprietà e metodi di chiamata. Per esempio:
foo?.bar = Bar()
Ancora una volta, qui non succederà niente di male se lo foo
è nil
. Il tuo codice continuerà semplicemente a essere eseguito.
Un altro bel trucco che il concatenamento opzionale ti consente di fare è controllare se l'impostazione di una proprietà o la chiamata di un metodo hanno avuto successo. Puoi farlo confrontando il valore restituito con nil
.
(Questo perché verrà restituito un valore opzionale Void?
anziché Void
su un metodo che non restituisce nulla)
Per esempio:
if (foo?.bar = Bar()) != nil {
print("bar was set successfully")
} else {
print("bar wasn’t set successfully")
}
Tuttavia, le cose diventano un po 'più complicate quando si tenta di accedere a proprietà o chiamare metodi che restituiscono un valore. Poiché foo
è opzionale, anche tutto ciò che viene restituito sarà opzionale. Per risolvere questo problema, è possibile scartare gli optionals che vengono restituiti utilizzando uno dei metodi precedenti oppure scartare foo
se stesso prima di accedere ai metodi o chiamare metodi che restituiscono valori.
Inoltre, come suggerisce il nome, puoi "concatenare" queste affermazioni insieme. Ciò significa che se foo
ha una proprietà opzionale baz
, che ha una proprietà qux
, puoi scrivere quanto segue:
let optionalQux = foo?.baz?.qux
Ancora una volta, poiché foo
e baz
sono opzionali, il valore restituito da qux
sarà sempre un opzionale indipendentemente dal fatto che sia esso qux
stesso opzionale.
map
e flatMap
Una caratteristica spesso sottoutilizzata con gli optional è la capacità di usare le funzioni map
e flatMap
. Questi consentono di applicare trasformazioni non facoltative a variabili facoltative. Se un facoltativo ha un valore, puoi applicarvi una data trasformazione. Se non ha un valore, rimarrà nil
.
Ad esempio, supponiamo di avere una stringa opzionale:
let anOptionalString:String?
Applicandovi la map
funzione, possiamo usare la stringByAppendingString
funzione per concatenarla a un'altra stringa.
Poiché stringByAppendingString
accetta un argomento stringa non facoltativo, non possiamo immettere direttamente la nostra stringa facoltativa. Tuttavia, utilizzando map
, possiamo usare consenti stringByAppendingString
da usare se anOptionalString
ha un valore.
Per esempio:
var anOptionalString:String? = "bar"
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // Optional("foobar")
Tuttavia, se anOptionalString
non ha un valore, map
tornerà nil
. Per esempio:
var anOptionalString:String?
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // nil
flatMap
funziona in modo simile a map
, tranne per il fatto che consente di restituire un altro optional dall'interno del corpo di chiusura. Ciò significa che puoi inserire un facoltativo in un processo che richiede un input non facoltativo, ma puoi emettere un facoltativo stesso.
try!
Il sistema di gestione degli errori di Swift può essere utilizzato in sicurezza con Do-Try-Catch :
do {
let result = try someThrowingFunc()
} catch {
print(error)
}
Se someThrowingFunc()
genera un errore, l'errore verrà catturato in modo sicuro nel catch
blocco.
La error
costante che vedi nel catch
blocco non è stata dichiarata da noi - è generata automaticamente da catch
.
Puoi anche dichiararti error
, ha il vantaggio di poterlo lanciare in un formato utile, ad esempio:
do {
let result = try someThrowingFunc()
} catch let error as NSError {
print(error.debugDescription)
}
L'utilizzo di try
questo metodo è il modo corretto per provare, rilevare e gestire gli errori derivanti dal lancio di funzioni.
C'è anche quello try?
che assorbe l'errore:
if let result = try? someThrowingFunc() {
// cool
} else {
// handle the failure, but there's no error information available
}
Ma il sistema di gestione degli errori di Swift fornisce anche un modo per "forzare il tentativo" con try!
:
let result = try! someThrowingFunc()
I concetti spiegati in questo post si applicano anche qui: se viene generato un errore, l'applicazione andrà in crash.
Dovresti usarlo solo try!
se puoi provare che il suo risultato non fallirà mai nel tuo contesto, e questo è molto raro.
La maggior parte delle volte utilizzerai il sistema completo Do-Try-Catch e quello opzionale try?
, nei rari casi in cui la gestione dell'errore non è importante.
Risorse
TL; Risposta DR
Con pochissime eccezioni , questa regola è d'oro:
Evita l'uso di !
Dichiarare la variabile facoltativa ( ?
), non gli optional scartati in modo implicito (IUO) ( !
)
In altre parole, usa piuttosto:
var nameOfDaughter: String?
Invece di:
var nameOfDaughter: String!
Scartare la variabile opzionale utilizzando if let
oguard let
O una variabile di scartare come questa:
if let nameOfDaughter = nameOfDaughter {
print("My daughters name is: \(nameOfDaughter)")
}
O in questo modo:
guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")
Questa risposta doveva essere concisa, per una piena comprensione leggi la risposta accettata
Risorse
Questa domanda viene fuori SEMPRE su SO. È una delle prime cose con cui i nuovi sviluppatori Swift lottano.
Sfondo:
Swift utilizza il concetto di "Optionals" per gestire valori che potrebbero contenere un valore o meno. In altri linguaggi come il C, potresti memorizzare un valore 0 in una variabile per indicare che non contiene alcun valore. Tuttavia, cosa succede se 0 è un valore valido? Quindi potresti usare -1. E se -1 è un valore valido? E così via.
Gli optionals Swift ti consentono di impostare una variabile di qualsiasi tipo per contenere un valore valido o nessun valore.
Metti un punto interrogativo dopo il tipo quando dichiari una variabile di significare (digita x o nessun valore).
Un opzionale è in realtà un contenitore che contiene una variabile di un dato tipo o niente.
Un optional deve essere "scartato" per poter recuperare il valore contenuto.
Il "!" operatore è un operatore di "scartamento forzato". Dice "fidati di me. So cosa sto facendo. Garantisco che quando questo codice viene eseguito, la variabile non conterrà zero". Se ti sbagli, crolli.
A meno che tu non sappia davvero cosa stai facendo, evita il "!" operatore di scartamento forzato. È probabilmente la più grande fonte di arresti anomali per i programmatori Swift alle prime armi.
Come trattare gli optional:
Ci sono molti altri modi per trattare gli optional che sono più sicuri. Eccone alcuni (elenco non esaustivo)
Puoi utilizzare "binding facoltativo" o "if let" per dire "se questo facoltativo contiene un valore, salva quel valore in una nuova variabile non facoltativa. Se facoltativo non contiene un valore, salta il corpo di questa istruzione if ".
Ecco un esempio di associazione facoltativa con il nostro foo
facoltativo:
if let newFoo = foo //If let is called optional binding. {
print("foo is not nil")
} else {
print("foo is nil")
}
Si noti che la variabile che si definisce quando si utilizza l'offerta facoltativa esiste solo (è solo "nell'ambito") nel corpo dell'istruzione if.
In alternativa, potresti usare un'istruzione guard, che ti consente di uscire dalla funzione se la variabile è nulla:
func aFunc(foo: Int?) {
guard let newFoo = input else { return }
//For the rest of the function newFoo is a non-optional var
}
Le istruzioni Guard sono state aggiunte in Swift 2. Guard ti consente di preservare il "percorso d'oro" attraverso il tuo codice ed evitare livelli sempre crescenti di if nidificati che a volte risultano dall'uso di binding opzionale "if let".
C'è anche un costrutto chiamato "operatore di coalescenza nullo". Ha la forma "optional_var ?? replacement_val". Restituisce una variabile non facoltativa con lo stesso tipo dei dati contenuti nell'opzionale. Se l'opzionale contiene nil, restituisce il valore dell'espressione dopo "??" simbolo.
Quindi potresti usare un codice come questo:
let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = \(newFoo)")
Puoi anche usare la gestione degli errori try / catch o guard, ma generalmente una delle altre tecniche sopra è più pulita.
MODIFICARE:
Un altro trucco leggermente più sottile con gli optionals è "optionals implicitamente scartati. Quando dichiariamo foo, potremmo dire:
var foo: String!
In tal caso foo è ancora un optional, ma non è necessario scartarlo per farvi riferimento. Ciò significa che ogni volta che provi a fare riferimento a foo, se è zero, ti arresti in modo anomalo.
Quindi questo codice:
var foo: String!
let upperFoo = foo.capitalizedString
Si bloccherà in riferimento alla proprietà capitalizedString di foo anche se non stiamo eseguendo lo scartamento forzato di foo. la stampa sembra a posto, ma non lo è.
Quindi vuoi stare molto attento con gli optionals implicitamente scartati. (e forse anche evitarli completamente finché non avrai una solida conoscenza degli optional.)
Conclusione: quando impari per la prima volta Swift, fingi il "!" il carattere non fa parte della lingua. È probabile che ti metta nei guai.
Poiché le risposte precedenti spiegano chiaramente come giocare in sicurezza con gli Optionals. Proverò a spiegare cosa sono realmente gli Optionals in modo rapido.
Un altro modo per dichiarare una variabile opzionale è
var i : Optional<Int>
E il tipo facoltativo non è altro che un'enumerazione con due casi, ad es
enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
.
.
.
}
Quindi, per assegnare uno zero alla nostra variabile 'i'. Possiamo fare
var i = Optional<Int>.none
o assegnare un valore, passeremo un valore
var i = Optional<Int>.some(28)
Secondo swift, "zero" è l'assenza di valore. E per creare un'istanza inizializzata con nil
Dobbiamo conformarci a un protocollo chiamato ExpressibleByNilLiteral
e fantastico, se lo indovinate, è sconsigliato Optionals
conformarsi ExpressibleByNilLiteral
e conformarsi ad altri tipi.
ExpressibleByNilLiteral
ha un unico metodo chiamato init(nilLiteral:)
che inizializza un'istanza con nil. Di solito non chiamerai questo metodo e, secondo la documentazione rapida, è sconsigliato chiamare questo inizializzatore direttamente poiché il compilatore lo chiama ogni volta che inizializzi un tipo opzionale con nil
letterale.
Anche me stesso devo avvolgere (nessun gioco di parole ) la mia testa intorno agli Optionals: D Happy Swfting All .
Innanzitutto, dovresti sapere cos'è un valore opzionale. Puoi passare a The Swift Programming Language per i dettagli.
Secondo, dovresti sapere che il valore facoltativo ha due stati. Uno è il valore pieno e l'altro è un valore nullo. Quindi, prima di implementare un valore opzionale, è necessario verificare in quale stato si trova.
Puoi usare if let ...
o guard let ... else
e così via.
In un altro modo, se non vuoi controllare lo stato della variabile prima della tua implementazione, puoi anche usare var buildingName = buildingName ?? "buildingName"
.
Ho riscontrato questo errore una volta quando stavo tentando di impostare i valori dei miei punti vendita dal metodo di preparazione per segue come segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DestinationVC{
if let item = sender as? DataItem{
// This line pops up the error
destination.nameLabel.text = item.name
}
}
}
Poi ho scoperto che non posso impostare i valori delle prese del controller di destinazione perché il controller non è stato ancora caricato o inizializzato.
Quindi l'ho risolto in questo modo:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DestinationVC{
if let item = sender as? DataItem{
// Created this method in the destination Controller to update its outlets after it's being initialized and loaded
destination.updateView(itemData: item)
}
}
}
Controller di destinazione:
// This variable to hold the data received to update the Label text after the VIEW DID LOAD
var name = ""
// Outlets
@IBOutlet weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
nameLabel.text = name
}
func updateView(itemDate: ObjectModel) {
name = itemDate.name
}
Spero che questa risposta aiuti chiunque abbia lo stesso problema in quanto ho scoperto che la risposta contrassegnata è una grande risorsa per la comprensione degli optional e del loro funzionamento, ma non ha affrontato direttamente il problema.
Fondamentalmente hai provato a utilizzare un valore nullo in luoghi in cui Swift consente solo quelli non nulli, dicendo al compilatore di fidarsi di te che non ci sarà mai valore nullo lì, consentendo così alla tua app di compilare.
Esistono diversi scenari che portano a questo tipo di errore fatale:
scarti forzati:
let user = someVariable!
Se
someVariable
è zero, si verificherà un arresto anomalo. Eseguendo uno scartamento forzato hai spostato la responsabilità del controllo zero dal compilatore a te, fondamentalmente facendo uno scartamento forzato stai garantendo al compilatore che non avrai mai valori zero lì. E indovina cosa succede se in qualche modo un valore nullo finisce insomeVariable
?Soluzione? Usa l'associazione opzionale (aka if-let), esegui l'elaborazione delle variabili lì:
if user = someVariable { // do your stuff }
lanci forzati (verso il basso):
let myRectangle = someShape as! Rectangle
Qui con il casting forzato dici al compilatore di non preoccuparti più, poiché avrai sempre
Rectangle
un'istanza lì. E fintanto che vale, non devi preoccuparti. I problemi iniziano quando tu oi tuoi colleghi del progetto iniziate a far circolare valori non rettangolari.Soluzione? Usa l'associazione opzionale (aka if-let), esegui l'elaborazione delle variabili lì:
if let myRectangle = someShape as? Rectangle { // yay, I have a rectangle }
Optionals implicitamente scartati. Supponiamo di avere la seguente definizione di classe:
class User { var name: String! init() { name = "(unnamed)" } func nicerName() { return "Mr/Ms " + name } }
Ora, se nessuno incasina la
name
proprietà impostandola sunil
, allora funziona come previsto, tuttavia seUser
viene inizializzato da un JSON a cui manca laname
chiave, si ottiene l'errore irreversibile quando si tenta di utilizzare la proprietà.Soluzione? Non usarli :) A meno che tu non sia sicuro al 102% che la proprietà avrà sempre un valore non nullo nel momento in cui dovrà essere utilizzata. Nella maggior parte dei casi, la conversione in opzionale o non opzionale funzionerà. Rendendolo non facoltativo, il compilatore ti aiuterà a indicare i percorsi del codice che hai perso dando un valore a quella proprietà
Prese scollegate o non ancora collegate. Questo è un caso particolare dello scenario n. 3. Fondamentalmente hai una classe caricata da XIB che vuoi usare.
class SignInViewController: UIViewController { @IBOutlet var emailTextField: UITextField! }
Ora, se ti sei perso il collegamento della presa dall'editor XIB, l'app si bloccherà non appena vorrai utilizzare la presa. Soluzione? Assicurati che tutte le prese siano collegate. Oppure utilizzare l'
?
operatore su di essi:emailTextField?.text = "[email protected]"
. Oppure dichiara l'outlet come opzionale, anche se in questo caso il compilatore ti costringerà a scartarlo in tutto il codice.Valori provenienti da Objective-C e che non hanno annotazioni nullability. Supponiamo di avere la seguente classe Objective-C:
@interface MyUser: NSObject @property NSString *name; @end
Ora, se non vengono specificate annotazioni nullability (esplicitamente o tramite
NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
), laname
proprietà verrà importata in Swift comeString!
(un IUO - opzionale scartato implicitamente). Non appena un codice swift vorrà utilizzare il valore, si bloccherà sename
è nullo.Soluzione? Aggiungi annotazioni nullability al tuo codice Objective-C. Attenzione però, il compilatore Objective-C è un po 'permissivo quando si tratta di nullability, potresti finire con valori nulli, anche se li hai contrassegnati esplicitamente come
nonnull
.
Questo è più un commento importante e spiega perché gli optionals implicitamente scartati possono essere ingannevoli quando si tratta di eseguire il debug dei nil
valori.
Pensa al seguente codice: si compila senza errori / avvisi:
c1.address.city = c3.address.city
Eppure in fase di esecuzione fornisce il seguente errore: Errore irreversibile: trovato inaspettatamente nullo durante lo scartamento di un valore opzionale
Puoi dirmi qual è l'oggetto nil
?
Non puoi!
Il codice completo sarebbe:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var c1 = NormalContact()
let c3 = BadContact()
c1.address.city = c3.address.city // compiler hides the truth from you and then you sudden get a crash
}
}
struct NormalContact {
var address : Address = Address(city: "defaultCity")
}
struct BadContact {
var address : Address!
}
struct Address {
var city : String
}
Per farla breve, usando var address : Address!
stai nascondendo la possibilità che una variabile possa provenire nil
da altri lettori. E quando si blocca dici "che diavolo ?! il mio address
non è un optional, quindi perché sto andando in crash?!.
Quindi è meglio scrivere come tale:
c1.address.city = c2.address!.city // ERROR: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Puoi ora dirmi quale oggetto era nil
?
Questa volta il codice ti è stato reso più chiaro. Puoi razionalizzare e pensare che probabilmente è il address
parametro che è stato scartato con forza.
Il codice completo sarebbe:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var c1 = NormalContact()
let c2 = GoodContact()
c1.address.city = c2.address!.city
c1.address.city = c2.address?.city // not compile-able. No deceiving by the compiler
c1.address.city = c2.address.city // not compile-able. No deceiving by the compiler
if let city = c2.address?.city { // safest approach. But that's not what I'm talking about here.
c1.address.city = city
}
}
}
struct NormalContact {
var address : Address = Address(city: "defaultCity")
}
struct GoodContact {
var address : Address?
}
struct Address {
var city : String
}
Gli errori EXC_BAD_INSTRUCTION
e fatal error: unexpectedly found nil while implicitly unwrapping an Optional value
appaiono di più quando hai dichiarato un @IBOutlet
, ma non connesso allo storyboard .
Dovresti anche imparare come funzionano gli Optionals , menzionato in altre risposte, ma questa è l'unica volta che mi sembra principalmente.
Se ricevi questo errore in CollectionView, prova a creare il file CustomCell e anche il file xib personalizzato.
aggiungi questo codice in ViewDidLoad () su mainVC.
let nib = UINib(nibName: "CustomnibName", bundle: nil)
self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
Mi sono imbattuto in questo errore durante il passaggio da un controller di visualizzazione tabella a un controller di visualizzazione perché avevo dimenticato di specificare il nome della classe personalizzato per il controller di visualizzazione nello storyboard principale.
Qualcosa di semplice che vale la pena controllare se tutto il resto sembra a posto