Che cosa significa "Errore irreversibile: trovato inaspettatamente zero durante lo scartamento di un valore opzionale"?


415

Il mio programma Swift si sta arrestando in modo anomalo EXC_BAD_INSTRUCTION uno dei seguenti errori simili. Cosa significa questo errore e come posso risolverlo?

Errore irreversibile: trovato inaspettatamente zero durante lo scartamento di un valore facoltativo

o

Errore irreversibile: trovato inaspettatamente zero mentre scartare implicitamente un valore Opzionale


Questo post ha lo scopo di raccogliere le risposte ai problemi "trovato 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.


8
@RobertColumbia, questa è una coppia di domande e risposte su Wiki della community con ampia documentazione del problema, non credo che questa domanda dovrebbe essere chiusa per mancanza di un MCVE.
JAL

Crea una variabile come questa [var nameOfDaughter: String? ] e usa quella variabile in questo modo [se let _ = nameOfDaughter {}] è l'approccio migliore.
iOS

Risposte:


674

Questa risposta è wiki della community . Se ritieni che potrebbe essere migliorato, sentiti libero di modificarlo !

Background: cos'è un optional?

In Swift, Optionalè un tipo generico che può contenere un valore (di qualsiasi tipo) o nessun valore.

In molti altri linguaggi di programmazione, un particolare valore "sentinella" viene spesso utilizzato per indicare la mancanza di un valore . In Objective-C, ad esempio, nil(il puntatore null ) indica la mancanza di un oggetto. Ma questo diventa più complicato quando si lavora con tipi primitivi - dovrebbe -1essere usato per indicare l'assenza di un numero intero, o forse INT_MIN, o di qualche altro numero intero? Se si sceglie un valore particolare per indicare "nessun numero intero", ciò significa che non può più essere trattato come un valore valido .

Swift è un linguaggio sicuro, il che significa che la lingua ti aiuta a essere chiaro sui tipi di valori con cui il tuo codice può lavorare. Se parte del codice prevede una stringa, digitare safety impedisce di passargli un Int per errore.

In Swift, qualsiasi tipo può essere reso facoltativo . Un valore facoltativo può assumere qualsiasi valore dal tipo originale o dal valore speciale nil.

Gli optionals sono definiti con un ?suffisso sul tipo:

var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int?    // `nil` is the default when no value is provided

La mancanza di un valore in un facoltativo è indicata da nil:

anOptionalInt = nil

(Nota che questo nilnon è lo stesso nildi Objective-C. In Objective-C nilè l'assenza di un puntatore oggetto valido ; in Swift, gli Optionals non sono limitati a oggetti / tipi di riferimento. Opzionale si comporta in modo simile a Forse di Haskell .)


Perché ho ricevuto " errore irreversibile: inaspettatamente trovato zero mentre scartavo un valore Opzionale "?

Per accedere a un valore opzionale (se ne ha uno), è necessario scartarlo . Un valore opzionale può essere scartato in modo sicuro o forzato. Se si scuote forzatamente un facoltativo e non aveva un valore, il programma si arresterà in modo anomalo con il messaggio sopra riportato.

Xcode ti mostrerà il crash evidenziando una riga di codice. Il problema si verifica su questa linea.

linea interrotta

Questo arresto può verificarsi con due diversi tipi di riavvolgimento forzato:

1. Espulsione forzata della forza

Questo viene fatto con l' !operatore su un optional. Per esempio:

let anOptionalString: String?
print(anOptionalString!) // <- CRASH

Errore irreversibile: trovato inaspettatamente zero durante lo scartamento di un valore facoltativo

Come anOptionalStringè nilqui, si verificherà un arresto anomalo sulla linea in cui viene forzato scartarlo.

2. Optionals implicitamente non confezionati

Questi sono definiti con un !, 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 accessorio implicitamente non scartato, questo verrà automaticamente forzato da scartare per te. Se non contiene un valore, si bloccherà.

print(optionalDouble) // <- CRASH

Errore irreversibile: trovato inaspettatamente zero mentre scartare implicitamente un valore Opzionale

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 opzionali implicitamente da scartare. Questo perché il tuo xib o storyboard collegherà le prese in fase di esecuzione, dopo l' inizializzazione. Dovresti quindi assicurarti di non accedere agli outlet prima che vengano caricati. Dovresti anche verificare che le connessioni siano corrette nel tuo storyboard / file xib, altrimenti i valori saranno nilin fase di esecuzione e quindi si arresterebbero in modo anomalo quando sono implicitamente non 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 Opzionale?

Espulsione forzata della forza

Come regola generale, non si dovrebbe mai forzare esplicitamente un involucro opzionale con l' !operatore. Ci possono essere casi in cui l'utilizzo! è accettabile, ma dovresti usarlo solo se sei sicuro al 100% che l'opzione contenga 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 confezionati

Queste variabili sono progettate in modo da poter differire la loro assegnazione fino a quando nel tuo codice. E ' la vostra responsabilità di assicurare che abbiano un valore prima di accedere. Tuttavia, poiché comportano lo scartamento forzato, sono ancora intrinsecamente non sicuri, poiché presumono che il valore sia diverso da zero, anche se l'assegnazione nulla è valida.

Dovresti usare solo gli optional implicitamente non scartati come ultima risorsa . Se puoi usare una variabile pigra o fornire un valore predefinito per una variabile, dovresti farlo invece di utilizzare un facoltativo involontariamente non scartato.

Tuttavia, ci sono alcuni scenari in cui gli opzionali implicitamente non scartati sono utili e si è ancora in grado di utilizzare vari modi per scartarli in modo sicuro come elencato di seguito - ma è sempre necessario utilizzarli con la dovuta cautela.


Come posso gestire in sicurezza gli Optionals?

Il modo più semplice per verificare se un'opzione contiene un valore è confrontarlo 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 optionals, si vorrebbe effettivamente accedere al valore che contiene, se ne contiene uno. Per fare ciò, è possibile utilizzare il binding opzionale .

Rilegatura opzionale

Il binding facoltativo consente di verificare se un optional contiene un valore e consente di assegnare il valore non imballato 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")
}

Quello che fa è prima verificare che l'opzionale contenga un valore. Se si fa , allora il valore 'scartare' è assegnato a una nuova variabile ( number) - che è quindi possibile utilizzare liberamente come se fosse non opzionale. Se l'opzione opzionale non contiene un valore, verrà invocata la clausola else, come previsto.

La cosa interessante di rilegatura opzionale è che puoi scartare più opzioni contemporaneamente. Puoi semplicemente separare le istruzioni con una virgola. La dichiarazione avrà esito positivo se tutti gli optionals non fossero 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 è che puoi anche usare le virgole per verificare una determinata 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 dell'associazione facoltativa all'interno di un'istruzione if è che è possibile accedere al valore non scartato solo all'interno dell'ambito dell'istruzione. Se è necessario accedere al valore al di fuori 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à a essere eseguito solo se tale condizione è soddisfatta. Sono definiti con la sintassi guard condition else {...}.

Quindi, per usarli con un'associazione opzionale, puoi farlo:

guard let number = anOptionalInt else {
    return
}

(Si noti che all'interno del corpo di guardia, è necessario utilizzare una delle istruzioni di trasferimento di controllo per uscire dall'ambito del codice attualmente in esecuzione).

Se anOptionalIntcontiene un valore, verrà scartato e assegnato alla nuova numbercostante. Il codice dopo la guardia continuerà quindi a essere eseguito. 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 dichiarazioni di guardia è che il valore non imballato è ora disponibile per l'uso nel codice che segue l'istruzione (come sappiamo che il codice futuro può essere eseguito solo se l'opzione ha un valore). Questo è un ottimo strumento per eliminare le "piramidi di sventura" create annidando più istruzioni if.

Per esempio:

guard let number = anOptionalInt else {
    return
}

print("anOptionalInt contains a value, and it’s: \(number)!")

Le guardie supportano anche gli stessi trucchi che supporta l'istruzione if, come scartare più opzioni contemporaneamente e usare la whereclausola.

Il fatto che tu usi un'istruzione if o guard dipende completamente dal fatto che qualsiasi codice futuro richieda che facoltativo contenga un valore.

Operatore di coalescenza zero

La coalescenza Operatore Nil è una versione abbreviata del nifty operatore condizionale ternario , in primo luogo progettato per convertire optional per i non-optional. Ha la sintassi a ?? b, dove aè un tipo opzionale ed bè dello stesso tipo di a(sebbene di solito non facoltativo).

In sostanza ti consente di dire “Se acontiene un valore, scartalo. Se invece non ritorna, binvece ”. Ad esempio, potresti usarlo in questo modo:

let number = anOptionalInt ?? 0

Questo definirà una numbercostante di Inttipo, che conterrà il valore di anOptionalInt, se contiene un valore, o 0altrimenti.

È solo una scorciatoia per:

let number = anOptionalInt != nil ? anOptionalInt! : 0

Concatenamento opzionale

È possibile utilizzare il concatenamento opzionale per chiamare un metodo o accedere a una proprietà su un facoltativo. Questo è semplicemente fatto con il suffisso del nome della variabile con un ?quando lo si utilizza.

Ad esempio, supponiamo di avere una variabile foo, di tipo Fooun'istanza facoltativa .

var foo : Foo?

Se volessimo chiamare un metodo fooche non restituisce nulla, possiamo semplicemente fare:

foo?.doSomethingInteresting()

Se foocontiene un valore, questo metodo verrà chiamato su di esso. In caso contrario, non accadrà nulla di negativo: il codice continuerà semplicemente a essere eseguito.

(Questo è un comportamento simile all'invio di messaggi nilin Objective-C)

Questo può quindi essere utilizzato anche per impostare proprietà e metodi di chiamata. Per esempio:

foo?.bar = Bar()

Anche in questo caso, nulla di male accadrà qui se fooè nil. Il tuo codice continuerà semplicemente a essere eseguito.

Un altro trucco utile che il concatenamento opzionale consente di verificare è se l'impostazione di una proprietà o la chiamata di un metodo hanno avuto esito positivo. È possibile farlo confrontando il valore restituito con nil.

(Questo perché verrà restituito un valore facoltativo Void?anziché Voidsu 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ù difficili quando si tenta di accedere a proprietà o chiamare metodi che restituiscono un valore. Poiché fooè facoltativo, tutto ciò che viene restituito sarà facoltativo. Per far fronte a questo, è possibile scartare gli optionals che vengono restituiti utilizzando uno dei metodi sopra indicati oppure scartare foose stessi prima di accedere ai metodi o chiamare metodi che restituiscono valori.

Inoltre, come suggerisce il nome, è possibile "concatenare" queste dichiarazioni insieme. Ciò significa che se fooha una proprietà opzionale baz, che ha una proprietà qux, è possibile scrivere quanto segue:

let optionalQux = foo?.baz?.qux

Ancora una volta, poiché fooe bazsono facoltativi, il valore restituito da quxsarà sempre facoltativo, indipendentemente dal fatto che quxsia facoltativo.

map e flatMap

Una caratteristica spesso sottoutilizzata con gli optional è la capacità di usare le funzioni mape flatMap. Ciò consente di applicare trasformazioni non opzionali a variabili opzionali. Se un'opzione ha un valore, è possibile applicare una determinata trasformazione ad essa. Se non ha un valore, rimarrà nil.

Ad esempio, supponiamo che tu abbia una stringa opzionale:

let anOptionalString:String?

Applicando la mapfunzione ad essa - possiamo usare la stringByAppendingStringfunzione per concatenarla a un'altra stringa.

Poiché stringByAppendingStringaccetta un argomento stringa non facoltativo, non possiamo inserire direttamente la nostra stringa opzionale. Tuttavia, usando map, possiamo usare consentire stringByAppendingStringdi essere usato se anOptionalStringha 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, mapverrà restituito 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 all'interno del corpo di chiusura. Ciò significa che è possibile inserire un opzionale in un processo che richiede un input non facoltativo, ma che può generare un opzionale stesso.

try!

Il sistema di gestione degli errori di Swift può essere tranquillamente utilizzato 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 catchblocco.

Il error costante che vedi nel catchblocco non è stata dichiarata da noi - è generata automaticamente da catch.

Puoi anche dichiarare error , ha il vantaggio di poterlo trasmettere in un formato utile, ad esempio:

do {
    let result = try someThrowingFunc()    
} catch let error as NSError {
    print(error.debugDescription)
}

L'utilizzo in tryquesto modo è il modo corretto di provare, rilevare e gestire gli errori derivanti dalle funzioni di lancio.

C'è anche 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 provare" con try!:

let result = try! someThrowingFunc()

I concetti spiegati in questo post si applicano anche qui: se viene generato un errore, l'applicazione si arresterà in modo anomalo.

Si dovrebbe usare sempre e solo try! se puoi provare che il suo risultato non fallirà mai nel tuo contesto - e questo è molto raro.

Il più delle volte utilizzerai il sistema Do-Try-Catch completo e quello opzionale try?, nei rari casi in cui la gestione dell'errore non è importante.


risorse


87
Personalmente, vorrei ringraziarti per esserti sforzato di scrivere tutto questo. Penso che questo sarà sicuramente utile sia per i principianti che per gli esperti che usano swift.
Pranav Wadhwa,

15
Sono contento che l'abbia trovato utile. Questa risposta è wiki della comunità, quindi è una collaborazione tra molte persone (7, finora)!
jtbandes,

1
Questo errore è intermittente nel mio caso. Eventuali suggerimenti? Codice in stackoverflow.com/questions/50933681/...
py_ios_dev

1
Forse è il momento di aggiornare questa risposta da usare al compactMap()posto di flatMap().
Nicolas Miari,

quindi immagino che la soluzione migliore e breve sia "Nil Coalescing Operator", giusto?
Mehdigriche,

65

TL; risposta DR

Con pochissime eccezioni , questa regola è d'oro:

Evitare l'uso di !

Dichiarare variabile opzionale ( ?), non implicitamente non scartato optionals (IUO) (! )

In altre parole, usa piuttosto:
var nameOfDaughter: String?

Invece di:
var nameOfDaughter: String!

Scartare la variabile opzionale usando if let oguard let

Scartare la variabile in questo modo:

if let nameOfDaughter = nameOfDaughter {
    print("My daughters name is: \(nameOfDaughter)")
}

O così:

guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")

Questa risposta doveva essere concisa, per la piena comprensione leggere la risposta accettata


risorse


40

Questa domanda si presenta 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 altre lingue come C, è possibile 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. Cosa succede se -1 è un valore valido? E così via.

Gli opzionali 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 come media (digita x o nessun valore).

Un opzionale è in realtà un contenitore che contiene una variabile di un determinato tipo o nulla.

Un opzionale deve essere "non scartato" per recuperare il valore all'interno.

Il "!" l'operatore è un operatore "forza di scartare". Dice "fidati di me. So cosa sto facendo. Garantisco che quando questo codice viene eseguito, la variabile non conterrà nulla". Se ti sbagli, ti schianti.

A meno che tu non sappia davvero cosa stai facendo, evita il "!" forzare l'operatore di scartare. È probabilmente la più grande fonte di arresti anomali per i programmatori Swift principianti.

Come trattare con gli opzionali:

Esistono molti altri modi di trattare con gli optional più sicuri. Eccone alcuni (non un elenco esaustivo)

Puoi usare "associazione opzionale" o "se lascia" dire "se questa opzione contiene un valore, salva quel valore in una nuova variabile non opzionale. Se l'opzione non contiene un valore, salta il corpo di questa istruzione if ".

Ecco un esempio di associazione opzionale con il nostro fooopzionale:

if let newFoo = foo //If let is called optional binding. {
  print("foo is not nil")
} else {
  print("foo is nil")
}

Nota che la variabile che definisci quando usi il biding opzionale esiste (è solo "nell'ambito") nel corpo dell'istruzione if.

In alternativa, è possibile utilizzare un'istruzione guard, che 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 dichiarazioni di Guard sono state aggiunte in Swift 2. Guard consente di preservare il "percorso d'oro" attraverso il codice ed evitare livelli sempre crescenti di if annidati che a volte risultano dall'uso dell'associazione opzionale "if let".

Esiste anche un costrutto chiamato "operatore a coalescenza zero". Prende la forma "optional_var ?? sostituzione_val". Restituisce una variabile non opzionale con lo stesso tipo dei dati contenuti nell'opzionale. Se l'opzione opzionale contiene zero, 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)")

È inoltre possibile utilizzare la gestione degli errori try / catch o guard, ma generalmente una delle altre tecniche sopra è più pulita.

MODIFICARE:

Un altro gotcha leggermente più sottile con gli optional è "optionals implicitamente non scartati. Quando dichiariamo foo, potremmo dire:

var foo: String!

In tal caso, foo è ancora opzionale, ma non è necessario scartarlo per fare riferimento. Ciò significa che ogni volta che provi a fare riferimento a foo, ti schianti se è zero.

Quindi questo codice:

var foo: String!


let upperFoo = foo.capitalizedString

Si arresterà in modo anomalo in riferimento alla proprietà String capitalizzata di foo anche se non stiamo forzando il foo. la stampa sembra a posto, ma non lo è.

Quindi vuoi stare molto attento con gli optional implicitamente da scartare. (e forse anche evitarli completamente fino a quando non avrai una solida conoscenza degli opzionali.)

In conclusione: quando apprendi Swift per la prima volta, fai finta di "!" il personaggio non fa parte della lingua. È probabile che ti metta nei guai.


7
Dovresti considerare di renderlo un wiki pubblico.
vacawama,

3
Modifica la tua risposta e sotto la casella di modifica a destra è presente una casella di controllo wiki della community . Non otterrai più un rappresentante per la risposta, ma so che questo non è comunque un palese rappresentante.
vacawama,

6
Dato che questa domanda viene posta un milione di volte sul sito, se avessi il tempo di pubblicare una domanda con risposta autonoma come risposta canonica alla domanda, spero che la risposta sia molto più completa di questa. Non c'è niente sulle guardclausole. Non c'è nulla sull'uso di if varquale sia un costrutto valido quanto if let. Non c'è nulla sulle whereclausole che ritengo degno di nota quando parliamo di if letrilegatura (in molti casi rimuove un intero strato di nidificazione). Non c'è nulla sul concatenamento opzionale.
nhgrif,

6
E quando menzioni gli opzionali implicitamente da scartare, non dici nemmeno che puoi usare tutti i trucchi opzionali sopra menzionati per scartare in sicurezza gli opzionali implicitamente da scartare. Inoltre, non copriamo le opzioni multiple non incluse nella stessa clausola.
nhgrif,

1
@nhgrif, ho detto "Potresti anche usare la gestione degli errori try / catch o guard, ma generalmente una delle altre tecniche sopra è più pulita." Hai sicuramente dei punti positivi. Questo è un argomento abbastanza ampio. Che ne dici di contribuire con la tua risposta che copre cose che non ho fatto piuttosto che fare commenti da cecchino? In questo modo la domanda e le sue risposte diventano una risorsa migliore per il sito.
Duncan C

13

Poiché le risposte di cui sopra spiegano chiaramente come giocare in sicurezza con Optionals. Proverò a spiegare quali Optionals sono davvero rapidi.

Un altro modo per dichiarare una variabile opzionale è

var i : Optional<Int>

E il tipo opzionale non è altro che un'enumerazione con due casi, vale a dire

 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 rapido, "zero" è l'assenza di valore. E per creare un'istanza inizializzata con nilDobbiamo conformarci a un protocollo chiamato ExpressibleByNilLiterale ottimo se hai indovinato, è sconsigliato solo Optionalsconformarsi ExpressibleByNilLiterale conformarsi ad altri tipi.

ExpressibleByNilLiteralha un solo metodo chiamato init(nilLiteral:)che inizializza un'istace con zero. Di solito non si chiama questo metodo e in base alla rapida documentazione è sconsigliato chiamare questo inizializzatore direttamente come il compilatore lo chiama ogni volta che si inizializza un tipo Opzionale con nilletterale.

Anche io stesso devo avvolgere (senza gioco di parole ) la mia testa attorno a Optionals: D Happy Swfting All .


12

Innanzitutto, dovresti sapere cos'è un valore opzionale. È possibile passare a Il linguaggio di programmazione Swift per i dettagli.

In secondo luogo, dovresti sapere che il valore opzionale ha due stati. Uno è il valore completo e l'altro è un valore zero. Quindi, prima di implementare un valore opzionale, è necessario verificare quale sia lo stato.

È possibile utilizzare if let ...o guard let ... elsecosì via.

In un altro modo, se non si desidera controllare lo stato della variabile prima dell'implementazione, è possibile utilizzare anche var buildingName = buildingName ?? "buildingName"invece.


8

Ho avuto questo errore una volta mentre stavo cercando 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
        }
    }
}

Quindi 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 là fuori con lo stesso problema che ho trovato la risposta contrassegnata è una grande risorsa per la comprensione degli opzionali e come funzionano, ma non ha affrontato direttamente il problema.


Basta non dimenticare di menzionare dove chiamare updateViewnel controller di destinazione;)
Ahmad F,

@AhmadF buon punto. Tuttavia, updateViewin questo caso non è necessario chiamare il controller di destinazione poiché sto usando la namevariabile per impostare nameLabel.textil file viewDidLoad. Ma se stessimo facendo un sacco di setup, sarebbe sicuramente meglio creare un'altra funzione facendolo e chiamandolo viewDidLoadinvece.
Wissa,

7

Fondamentalmente hai provato a utilizzare un valore nullo in luoghi in cui Swift consente solo quelli non nulli, dicendo al compilatore di fidarti di te che non ci sarà mai un valore nullo lì, consentendo così la compilazione della tua app.

Esistono diversi scenari che portano a questo tipo di errore fatale:

  1. involucri forzati:

    let user = someVariable!

    Se someVariableè zero, otterrai un arresto anomalo. Effettuando una rimozione della forza hai spostato la responsabilità del controllo zero dal compilatore a te, fondamentalmente eseguendo un svolgimento forzato stai garantendo al compilatore che non avrai mai valori nulli lì. E indovina cosa succede se in qualche modo finisce un valore nullo in someVariable?

    Soluzione? Usa un'associazione opzionale (aka if-let), esegui l'elaborazione variabile lì:

    if user = someVariable {
        // do your stuff
    }
  2. cast forzati (verso il basso):

    let myRectangle = someShape as! Rectangle

    Qui con il casting forzato dici al compilatore di non preoccuparti più, dato che avrai sempre Rectangleun'istanza lì. E finché ciò vale, non devi preoccuparti. I problemi iniziano quando tu o i tuoi colleghi del progetto iniziate a far circolare valori non rettangolari.

    Soluzione? Usa un'associazione opzionale (aka if-let), esegui l'elaborazione variabile lì:

    if let myRectangle = someShape as? Rectangle {
        // yay, I have a rectangle
    }
  3. Optionals implicitamente non scartati. Supponiamo che tu abbia la seguente definizione di classe:

    class User {
        var name: String!
    
        init() {
            name = "(unnamed)"
        }
    
        func nicerName() {
            return "Mr/Ms " + name
        }
    }

    Ora, se nessuno confonde con la nameproprietà impostandola su nil, allora funziona come previsto, tuttavia se Userviene inizializzato da un JSON privo diname chiave, viene visualizzato 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 diverso da zero al momento in cui deve essere utilizzata. Nella maggior parte dei casi, la conversione in opzionale o non opzionale funzionerà. Se lo rendi non facoltativo, il compilatore ti aiuterà anche indicandoti i percorsi del codice che hai perso e dando un valore a quella proprietà

  4. Prese non connesse o non ancora connesse. Questo è un caso particolare dello scenario n. 3. Fondamentalmente hai qualche classe XIB caricata che vuoi usare.

    class SignInViewController: UIViewController {
    
        @IBOutlet var emailTextField: UITextField!
    }

    Ora se ti sei perso a connettere la presa dall'editor XIB, l'app andrà in crash non appena vorrai utilizzare la presa. Soluzione? Assicurarsi che tutte le prese siano collegate. Oppure utilizzare l' ?operatore su di essi: emailTextField?.text = "my@email.com". O dichiarare lo sbocco come facoltativo, anche se in questo caso il compilatore ti costringerà a scartarlo tutto il codice.

  5. Valori provenienti da Objective-C e che non hanno annotazioni di nullability. Supponiamo di avere la seguente classe Objective-C:

    @interface MyUser: NSObject
    @property NSString *name;
    @end

    Ora se non vengono specificate annotazioni di nullità (esplicitamente o tramite NS_ASSUME_NONNULL_BEGIN/ NS_ASSUME_NONNULL_END), la nameproprietà verrà importata in Swift come String!(un IUO - facoltativamente non imballato facoltativo). Non appena un codice rapido vorrà utilizzare il valore, si bloccherà se nameè zero.

    Soluzione? Aggiungi annotazioni di nullability al tuo codice Objective-C. Attenzione, 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.


2

Questo è più un commento importante e che il motivo per cui gli opzionali implicitamente non scartati possono essere ingannevoli quando si tratta di nilvalori di debug .

Pensa al seguente codice: si compila senza errori / avvisi:

c1.address.city = c3.address.city

Tuttavia in fase di esecuzione genera il seguente errore: Errore irreversibile: trovato inaspettatamente zero durante lo scartamento di un valore opzionale

Puoi dirmi quale 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 essere nildi altri lettori. E quando si blocca sei tipo "che diavolo ?! il mio addressnon è 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 

Ora puoi dirmi quale oggetto era nil?

Questa volta il codice ti è stato reso più chiaro. Puoi razionalizzare e pensare che probabilmente è il addressparametro che è stato forzatamente scartato.

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
}

2

Gli errori EXC_BAD_INSTRUCTIONe fatal error: unexpectedly found nil while implicitly unwrapping an Optional valueappare di più quando hai dichiarato un @IBOutlet, ma non connesso allo storyboard .

Dovresti anche sapere come funzionano gli Optionals , menzionati in altre risposte, ma questa è l'unica volta che mi appare principalmente.


la @IBOutletcausa di questo errore non dovrebbe avere l' errore irreversibile: trovato inaspettatamente zero mentre scartando implicitamente una versione con valore opzionale dell'errore?
pkamb,

1
È vero. Forse quando invio la risposta è quello che volevo dire e copio incollato il primo messaggio di errore fatale. La risposta di Hamish sembra molto completa al riguardo.
Ale Mohamad,

2

Se ricevi questo errore in CollectionView prova a creare anche il file CustomCell e Custom xib.

aggiungi questo codice in ViewDidLoad () su mainVC.

    let nib = UINib(nibName: "CustomnibName", bundle: nil)
    self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")

0

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 personalizzata per il controller di visualizzazione nello storyboard principale.

Qualcosa di semplice che vale la pena verificare se tutto il resto sembra a posto

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.