Was bedeutet "Schwerwiegender Fehler: Beim Auspacken eines optionalen Werts unerwartet Null gefunden"?

Aug 24 2015

Mein Swift-Programm stürzt mit EXC_BAD_INSTRUCTIONeinem der folgenden ähnlichen Fehler ab. Was bedeutet dieser Fehler und wie behebe ich ihn?

Schwerwiegender Fehler: Beim Auspacken eines optionalen Werts wurde unerwartet Null gefunden

oder

Schwerwiegender Fehler: Unerwartet wurde Null gefunden, während implizit ein optionaler Wert entpackt wurde


Dieser Beitrag soll Antworten auf "unerwartet gefundene Null" -Probleme sammeln, damit sie nicht verstreut und schwer zu finden sind. Fühlen Sie sich frei, Ihre eigene Antwort hinzuzufügen oder die vorhandene Wiki-Antwort zu bearbeiten .

Antworten

685 Hamish Aug 24 2015 at 02:10

Diese Antwort ist Community-Wiki . Wenn Sie der Meinung sind, dass es besser gemacht werden könnte, können Sie es jederzeit bearbeiten !

Hintergrund: Was ist optional?

In Swift Optional<Wrapped>handelt es sich um einen Optionstyp : Er kann einen beliebigen Wert aus dem ursprünglichen Typ ("Wrapped") oder überhaupt keinen Wert (den Sonderwert nil) enthalten. Ein optionaler Wert muss entpackt werden, bevor er verwendet werden kann.

Optional ist ein generischer Typ , was bedeutet , dass Optional<Int>und Optional<String>sind verschiedene Typen - die Art innerhalb <>des umschlossenen Typ genannt wird. Unter der Haube ist ein Optional eine Aufzählung mit zwei Fällen: .some(Wrapped)und .none, wo .noneentspricht nil.

Optionale Optionen können mit dem angegebenen Typ Optional<T>oder (am häufigsten) als Kurzform mit einem ?Suffix deklariert werden .

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

Optionals sind ein einfaches, aber leistungsstarkes Tool, mit dem Sie Ihre Annahmen beim Schreiben von Code ausdrücken können. Der Compiler kann diese Informationen verwenden, um Fehler zu vermeiden. Aus der Swift-Programmiersprache :

Swift ist eine typsichere Sprache, dh die Sprache hilft Ihnen dabei, klar zu machen, mit welchen Arten von Werten Ihr Code arbeiten kann. Wenn ein Teil Ihres Codes a erfordert String, verhindert die Typensicherheit, dass Sie ihn Intversehentlich weitergeben. Ebenso verhindert die Typensicherheit, dass Sie versehentlich ein optionales Element Stringan einen Code übergeben, für den ein nicht optionales Element erforderlich ist String. Die Typensicherheit hilft Ihnen, Fehler so früh wie möglich im Entwicklungsprozess zu erkennen und zu beheben.

Einige andere Programmiersprachen haben ebenfalls generische Optionstypen : z. B. Vielleicht in Haskell, Option in Rust und optional in C ++ 17.

In Programmiersprachen ohne Optionstypen wird häufig ein bestimmter "Sentinel" -Wert verwendet, um das Fehlen eines gültigen Werts anzuzeigen. In Objective-C beispielsweise repräsentiert nil(der Nullzeiger ) das Fehlen eines Objekts. Für primitive Typen wie intkann kein Nullzeiger verwendet werden, sodass Sie entweder eine separate Variable (wie value: Intund isValid: Bool) oder einen bestimmten Sentinel-Wert (wie -1oder INT_MIN) benötigen . Diese Ansätze sind fehleranfällig, da leicht vergessen wird, isValidden Sentinel-Wert zu überprüfen oder zu überprüfen. Wenn ein bestimmter Wert als Sentinel ausgewählt wird, bedeutet dies, dass er nicht mehr als gültiger Wert behandelt werden kann.

Optionstypen wie Swift Optionallösen diese Probleme, indem sie einen speziellen, separaten nilWert einführen (damit Sie keinen Sentinel-Wert festlegen müssen) und das System mit starken Typen nutzen, damit der Compiler Sie bei Bedarf daran erinnern kann, nach Null zu suchen.


Warum wurde " schwerwiegender Fehler: Beim Auspacken eines optionalen Werts unerwartet Null gefunden" angezeigt ?

Um auf den Wert eines optionalen Elements zuzugreifen (falls überhaupt vorhanden), müssen Sie ihn auspacken . Ein optionaler Wert kann sicher oder gewaltsam ausgepackt werden. Wenn Sie eine Option zwangsweise auspacken und sie keinen Wert hat, stürzt Ihr Programm mit der obigen Meldung ab.

Xcode zeigt Ihnen den Absturz an, indem Sie eine Codezeile markieren. Das Problem tritt in dieser Zeile auf.

Dieser Absturz kann mit zwei verschiedenen Arten des Force-Unwraps auftreten:

1. Explizite Kraftentpackung

Dies erfolgt mit dem !Bediener optional. Zum Beispiel:

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

Schwerwiegender Fehler: Beim Auspacken eines optionalen Werts wurde unerwartet Null gefunden

Wie anOptionalStringist nilhier, Sie werden einen Absturz auf der Linie, wo Sie unwrap es erzwingen.

2. Implizit ausgepackte Optionen

Diese werden !eher mit einem als mit einem ?nach dem Typ definiert.

var optionalDouble: Double!   // this value is implicitly unwrapped wherever it's used

Es wird angenommen, dass diese Optionen einen Wert enthalten. Wenn Sie daher auf eine implizit entpackte Option zugreifen, wird diese automatisch für Sie erzwungen. Wenn es keinen Wert enthält, stürzt es ab.

print(optionalDouble) // <- CRASH

Schwerwiegender Fehler: Unerwartet wurde Null gefunden, während implizit ein optionaler Wert entpackt wurde

Um herauszufinden, welche Variable den Absturz verursacht hat, können Sie gedrückt halten, während Sie auf klicken, um die Definition anzuzeigen, in der Sie möglicherweise den optionalen Typ finden.

Insbesondere IBOutlets sind normalerweise implizit ausgepackte Optionen. Dies liegt daran, dass Ihr xib oder Storyboard die Outlets nach der Initialisierung zur Laufzeit verbindet . Sie sollten daher sicherstellen, dass Sie nicht auf Outlets zugreifen, bevor diese geladen werden. Sie sollten auch überprüfen, ob die Verbindungen in Ihrer Storyboard- / XIB-Datei korrekt sind. Andernfalls werden die Werte nilzur Laufzeit angezeigt und stürzen daher ab, wenn sie implizit entpackt werden . Versuchen Sie beim Reparieren von Verbindungen, die Codezeilen zu löschen, die Ihre Steckdosen definieren, und verbinden Sie sie dann erneut.


Wann sollte ich jemals ein optionales Auspacken erzwingen?

Explizite Force-Entpackung

In der Regel sollten Sie niemals explizit das Entpacken einer Option mit dem !Operator erzwingen . Es kann Fälle geben, in denen die Verwendung !akzeptabel ist. Sie sollten sie jedoch nur verwenden, wenn Sie zu 100% sicher sind, dass die Option einen Wert enthält.

Zwar kann es vorkommen, dass Sie das gewaltsame Auspacken verwenden können, da Sie wissen , dass ein optionales Element einen Wert enthält. Es gibt jedoch keinen einzigen Ort, an dem Sie dieses optionale Element nicht sicher auspacken können.

Implizit ausgepackte Optionen

Diese Variablen sind so konzipiert, dass Sie ihre Zuordnung bis zu einem späteren Zeitpunkt in Ihrem Code verschieben können. Es liegt in Ihrer Verantwortung, sicherzustellen, dass sie einen Wert haben, bevor Sie darauf zugreifen. Da es sich jedoch um ein gewaltsames Auspacken handelt, sind sie von Natur aus immer noch unsicher, da davon ausgegangen wird, dass Ihr Wert nicht Null ist, obwohl die Zuweisung von Null gültig ist.

Sie sollten nur implizit ausgepackte Optionen als letzten Ausweg verwenden . Wenn Sie eine verzögerte Variable verwenden oder einen Standardwert für eine Variable angeben können, sollten Sie dies tun, anstatt eine implizit entpackte Option zu verwenden.

Es gibt jedoch einige Szenarien, in denen implizit ausgepackte Optionen von Vorteil sind , und Sie können weiterhin verschiedene Methoden zum sicheren Auspacken verwenden, wie unten aufgeführt. Sie sollten sie jedoch immer mit der gebotenen Vorsicht verwenden.


Wie kann ich sicher mit Optionals umgehen?

Der einfachste Weg, um zu überprüfen, ob ein optionales Element einen Wert enthält, besteht darin, ihn mit zu vergleichen nil.

if anOptionalInt != nil {
    print("Contains a value!")
} else {
    print("Doesn’t contain a value.")
}

In 99,9% der Fälle, in denen Sie mit Optionen arbeiten, möchten Sie tatsächlich auf den darin enthaltenen Wert zugreifen, sofern dieser überhaupt einen enthält. Dazu können Sie die optionale Bindung verwenden .

Optionale Bindung

Mit der optionalen Bindung können Sie überprüfen, ob eine Option einen Wert enthält - und den nicht umschlossenen Wert einer neuen Variablen oder Konstante zuweisen. Es verwendet die Syntax if let x = anOptional {...}oder if var x = anOptional {...}, je nachdem, ob Sie den Wert der neuen Variablen nach dem Binden ändern müssen.

Zum Beispiel:

if let number = anOptionalInt {
    print("Contains a value! It is \(number)!")
} else {
    print("Doesn’t contain a number")
}

Überprüfen Sie zunächst, ob das optionale Element einen Wert enthält. Wenn dies der Fall ist , wird der Wert "entpackt" einer neuen Variablen ( number) zugewiesen, die Sie dann frei verwenden können, als wäre sie nicht optional. Wenn das optionale Element keinen Wert enthält, wird die else-Klausel wie erwartet aufgerufen.

Das Schöne an der optionalen Bindung ist, dass Sie mehrere Optionen gleichzeitig auspacken können. Sie können die Anweisungen einfach durch ein Komma trennen. Die Anweisung ist erfolgreich, wenn alle Optionen ausgepackt wurden.

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")
}

Ein weiterer guter Trick ist, dass Sie auch Kommas verwenden können, um nach dem Auspacken nach einer bestimmten Bedingung für den Wert zu suchen.

if let number = anOptionalInt, number > 0 {
    print("anOptionalInt contains a value: \(number), and it’s greater than zero!")
}

Der einzige Haken bei der Verwendung der optionalen Bindung in einer if-Anweisung besteht darin, dass Sie nur innerhalb des Bereichs der Anweisung auf den nicht umschlossenen Wert zugreifen können. Wenn Sie außerhalb des Gültigkeitsbereichs der Anweisung Zugriff auf den Wert benötigen, können Sie eine Guard-Anweisung verwenden .

Mit einer Guard-Anweisung können Sie eine Bedingung für den Erfolg definieren. Der aktuelle Bereich wird nur dann weiter ausgeführt, wenn diese Bedingung erfüllt ist. Sie werden mit der Syntax definiert guard condition else {...}.

Um sie mit einer optionalen Bindung zu verwenden, können Sie Folgendes tun:

guard let number = anOptionalInt else {
    return
}

(Beachten Sie, dass innerhalb des Schutzkörpers, Sie müssen eine der Verwendung Steuertransferanweisungen , um den Umfang des aktuell ausgeführten Code zu verlassen).

Wenn anOptionalIntein Wert enthalten ist, wird er entpackt und der neuen numberKonstante zugewiesen . Der Code nach dem Guard wird dann weiter ausgeführt. Wenn es keinen Wert enthält, führt der Guard den Code in den Klammern aus, was zur Übertragung der Kontrolle führt, sodass der Code unmittelbar danach nicht ausgeführt wird.

Das Schöne an Guard-Anweisungen ist, dass der nicht umschlossene Wert jetzt in Code verwendet werden kann, der auf die Anweisung folgt (da wir wissen, dass zukünftiger Code nur ausgeführt werden kann , wenn das optionale einen Wert hat). Dies ist ideal, um "Pyramiden des Untergangs" zu eliminieren , die durch das Verschachteln mehrerer if-Anweisungen entstehen.

Zum Beispiel:

guard let number = anOptionalInt else {
    return
}

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

Guards unterstützen auch dieselben netten Tricks wie die if-Anweisung, z. B. das gleichzeitige Auspacken mehrerer Optionen und die Verwendung der whereKlausel.

Egal , ob Sie eine if oder Schutzerklärung verwenden , hängt vollständig ab , ob ein künftiges Code erfordert die optionalen Wert enthalten.

Null-Koaleszenz-Operator

Der Nil Coalescing Operator ist eine raffinierte Kurzversion des ternären bedingten Operators , der hauptsächlich zum Konvertieren von Optionen in Nicht-Optionen entwickelt wurde. Es hat die Syntax a ?? b, wobei aes sich um einen optionalen Typ handelt und bder gleiche Typ ist wie a(obwohl normalerweise nicht optional).

Sie können im Wesentlichen sagen: „Wenn aein Wert enthalten ist, packen Sie ihn aus. Wenn dies nicht der Fall ist, kehren Sie bstattdessen zurück. “ Zum Beispiel könnten Sie es so verwenden:

let number = anOptionalInt ?? 0

Dadurch wird eine numberKonstante vom IntTyp definiert, die entweder den Wert von anOptionalIntenthält, wenn sie einen Wert enthält, oder auf 0andere Weise.

Es ist nur eine Abkürzung für:

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

Optionale Verkettung

Sie können die optionale Verkettung verwenden, um eine Methode aufzurufen oder auf eine optionale Eigenschaft zuzugreifen. Dies geschieht einfach, indem der Variablenname ?bei Verwendung mit einem Suffix versehen wird .

Angenommen, wir haben eine Variable foovom Typ einer optionalen FooInstanz.

var foo : Foo?

Wenn wir eine Methode aufrufen wollten foo, die nichts zurückgibt, können wir einfach Folgendes tun:

foo?.doSomethingInteresting()

Wenn fooein Wert enthalten ist, wird diese Methode darauf aufgerufen. Wenn dies nicht der Fall ist, passiert nichts Schlimmes - der Code wird einfach weiter ausgeführt.

(Dies ähnelt dem Senden von Nachrichten an nilObjective-C.)

Dies kann daher auch zum Festlegen von Eigenschaften sowie von Aufrufmethoden verwendet werden. Zum Beispiel:

foo?.bar = Bar()

Auch hier wird nichts Schlimmes passieren, wenn es so fooist nil. Ihr Code wird einfach weiter ausgeführt.

Ein weiterer nützlicher Trick, den Sie mit der optionalen Verkettung ausführen können, besteht darin, zu überprüfen, ob das Festlegen einer Eigenschaft oder das Aufrufen einer Methode erfolgreich war. Sie können dies tun, indem Sie den Rückgabewert mit vergleichen nil.

(Dies liegt daran, dass ein optionaler Wert zurückgegeben wird Void?und nicht Voidfür eine Methode, die nichts zurückgibt.)

Zum Beispiel:

if (foo?.bar = Bar()) != nil {
    print("bar was set successfully")
} else {
    print("bar wasn’t set successfully")
}

Beim Versuch, auf Eigenschaften zuzugreifen oder Methoden aufzurufen, die einen Wert zurückgeben, wird es jedoch etwas schwieriger. Da dies foooptional ist, ist alles, was von ihm zurückgegeben wird, ebenfalls optional. Um dies zu beheben, können Sie entweder die zurückgegebenen Optionen mit einer der oben genannten Methoden auspacken oder sich fooselbst auspacken , bevor Sie auf Methoden zugreifen oder Methoden aufrufen, die Werte zurückgeben.

Wie der Name schon sagt, können Sie diese Anweisungen auch miteinander verketten. Dies bedeutet, dass Sie, wenn Sie fooeine optionale Eigenschaft haben baz, die eine Eigenschaft quxhat, Folgendes schreiben können:

let optionalQux = foo?.baz?.qux

Da foound bazoptional sind, ist der von zurückgegebene Wert quximmer optional, unabhängig davon, ob er quxselbst optional ist.

map und flatMap

Eine häufig nicht genutzte Funktion mit Optionen ist die Möglichkeit, die Funktionen mapund zu flatMapverwenden. Mit diesen können Sie nicht optionale Transformationen auf optionale Variablen anwenden. Wenn eine Option einen Wert hat, können Sie eine bestimmte Transformation darauf anwenden. Wenn es keinen Wert hat, bleibt es nil.

Angenommen, Sie haben eine optionale Zeichenfolge:

let anOptionalString:String?

Durch Anwenden der mapFunktion darauf können wir die stringByAppendingStringFunktion verwenden, um sie mit einer anderen Zeichenfolge zu verketten.

Da stringByAppendingStringein nicht optionales Zeichenfolgenargument verwendet wird, können wir unsere optionale Zeichenfolge nicht direkt eingeben. Mit using mapkönnen wir jedoch allow stringByAppendingStringverwenden, wenn anOptionalStringein Wert vorhanden ist.

Zum Beispiel:

var anOptionalString:String? = "bar"

anOptionalString = anOptionalString.map {unwrappedString in
    return "foo".stringByAppendingString(unwrappedString)
}

print(anOptionalString) // Optional("foobar")

Wenn anOptionalStringjedoch kein Wert vorhanden ist, mapwird zurückgegeben nil. Zum Beispiel:

var anOptionalString:String?

anOptionalString = anOptionalString.map {unwrappedString in
    return "foo".stringByAppendingString(unwrappedString)
}

print(anOptionalString) // nil

flatMapfunktioniert ähnlich wie map, außer dass Sie eine weitere Option aus dem Verschlusskörper zurückgeben können. Dies bedeutet, dass Sie eine Option in einen Prozess eingeben können, für den eine nicht optionale Eingabe erforderlich ist, eine Option jedoch selbst ausgeben kann.

try!

Das Fehlerbehandlungssystem von Swift kann sicher mit Do-Try-Catch verwendet werden :

do {
    let result = try someThrowingFunc() 
} catch {
    print(error)
}

Wenn someThrowingFunc()ein Fehler ausgelöst wird, wird der Fehler sicher im catchBlock abgefangen .

Die errorKonstante, die Sie im catchBlock sehen, wurde von uns nicht deklariert - sie wird automatisch von generiert catch.

Sie können sich auch errorselbst deklarieren , es hat den Vorteil, dass Sie es in ein nützliches Format umwandeln können, zum Beispiel:

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

Auf trydiese Weise können Sie Fehler, die durch Wurffunktionen verursacht werden, richtig versuchen, abfangen und behandeln.

Es gibt auch, try?was den Fehler absorbiert:

if let result = try? someThrowingFunc() {
    // cool
} else {
    // handle the failure, but there's no error information available
}

Das Fehlerbehandlungssystem von Swift bietet jedoch auch eine Möglichkeit, den Versuch zu erzwingen try!:

let result = try! someThrowingFunc()

Die in diesem Beitrag erläuterten Konzepte gelten auch hier: Wenn ein Fehler ausgegeben wird, stürzt die Anwendung ab.

Sie sollten es nur verwenden, try!wenn Sie nachweisen können, dass das Ergebnis in Ihrem Kontext niemals fehlschlägt - und dies ist sehr selten.

Meistens verwenden Sie das komplette Do-Try-Catch-System - und das optionale try?- in den seltenen Fällen, in denen die Behandlung des Fehlers nicht wichtig ist.


Ressourcen

65 Sajjon Dec 19 2016 at 20:26

TL; DR Antwort

Mit sehr wenigen Ausnahmen ist diese Regel golden:

Vermeiden Sie die Verwendung von !

Deklarieren Sie die Variable optional ( ?), nicht implizit entpackte Optionals (IUO) ( !)

Mit anderen Worten, verwenden Sie lieber:
var nameOfDaughter: String?

Anstatt:
var nameOfDaughter: String!

Entpacken Sie die optionale Variable mit if letoderguard let

Entweder die Variable wie folgt auspacken:

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

Oder so:

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

Diese Antwort sollte kurz und prägnant sein. Lesen Sie die akzeptierte Antwort , um sie vollständig zu verstehen


Ressourcen

40 DuncanC Feb 28 2016 at 21:37

Diese Frage taucht die ganze Zeit bei SO auf. Es ist eines der ersten Dinge, mit denen neue Swift-Entwickler zu kämpfen haben.

Hintergrund:

Swift verwendet das Konzept der "Optionals", um mit Werten umzugehen, die einen Wert enthalten könnten oder nicht. In anderen Sprachen wie C können Sie den Wert 0 in einer Variablen speichern, um anzuzeigen, dass sie keinen Wert enthält. Was ist jedoch, wenn 0 ein gültiger Wert ist? Dann könnten Sie -1 verwenden. Was ist, wenn -1 ein gültiger Wert ist? Und so weiter.

Mit Swift-Optionen können Sie eine Variable eines beliebigen Typs so einrichten, dass sie entweder einen gültigen Wert oder keinen Wert enthält.

Sie setzen ein Fragezeichen nach dem Typ, wenn Sie eine Variable als Mittelwert deklarieren (Typ x oder kein Wert).

Ein optionales Element ist tatsächlich ein Container, der entweder eine Variable eines bestimmten Typs oder nichts enthält.

Ein optionales Element muss "ausgepackt" werden, um den Wert darin abzurufen.

Das "!" operator ist ein "Force Unwrap" -Operator. Es heißt "Vertrau mir. Ich weiß, was ich tue. Ich garantiere, dass die Variable bei der Ausführung dieses Codes kein Null enthält." Wenn Sie sich irren, stürzen Sie ab.

Vermeiden Sie das "!", Wenn Sie nicht wirklich wissen , was Sie tun. Bediener zum Auspacken zwingen. Es ist wahrscheinlich die größte Absturzquelle für Anfänger von Swift-Programmierern.

Umgang mit Optionen:

Es gibt viele andere Möglichkeiten, mit Optionen umzugehen, die sicherer sind. Hier sind einige (keine vollständige Liste)

Sie können "optionale Bindung" oder "if let" verwenden, um zu sagen, "wenn diese Option einen Wert enthält, speichern Sie diesen Wert in einer neuen, nicht optionalen Variablen. Wenn die Option keinen Wert enthält, überspringen Sie den Hauptteil dieser if-Anweisung ".

Hier ist ein Beispiel für eine optionale Bindung mit unserem foooptionalen:

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

Beachten Sie, dass die Variable, die Sie definieren, wenn Sie optionales Biding verwenden, nur im Hauptteil der if-Anweisung vorhanden ist (nur "im Gültigkeitsbereich" ist).

Alternativ können Sie eine Guard-Anweisung verwenden, mit der Sie Ihre Funktion beenden können, wenn die Variable Null ist:

func aFunc(foo: Int?) {
  guard let newFoo = input else { return }
  //For the rest of the function newFoo is a non-optional var
}

Guard-Anweisungen wurden in Swift 2 hinzugefügt. Mit Guard können Sie den "goldenen Pfad" durch Ihren Code beibehalten und immer mehr verschachtelte ifs vermeiden, die manchmal aus der Verwendung der optionalen Bindung "if let" resultieren.

Es gibt auch ein Konstrukt, das als "Null-Koaleszenz-Operator" bezeichnet wird. Es hat die Form "optional_var ?? replace_val". Es gibt eine nicht optionale Variable mit demselben Typ wie die im optionalen enthaltenen Daten zurück. Wenn das optionale Element nil enthält, wird der Wert des Ausdrucks nach dem "??" Symbol.

Sie könnten also folgenden Code verwenden:

let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = \(newFoo)")

Sie können auch die Fehlerbehandlung try / catch oder guard verwenden, aber im Allgemeinen ist eine der anderen oben genannten Techniken sauberer.

BEARBEITEN:

Ein anderes, etwas subtileres Problem mit Optionen ist "implizit ausgepackte Optionen". Wenn wir foo deklarieren, können wir sagen:

var foo: String!

In diesem Fall ist foo immer noch optional, aber Sie müssen es nicht auspacken, um darauf zu verweisen. Das bedeutet, dass Sie jedes Mal, wenn Sie versuchen, auf foo zu verweisen, abstürzen, wenn es gleich Null ist.

Also dieser Code:

var foo: String!


let upperFoo = foo.capitalizedString

Wird beim Verweis auf die Eigenschaft "kapitalisiertes String" von foo abstürzen, obwohl wir foo nicht zwangsweise auspacken. Der Druck sieht gut aus, ist es aber nicht.

Daher möchten Sie mit implizit ausgepackten Optionen sehr vorsichtig sein. (und vielleicht sogar ganz vermeiden, bis Sie ein solides Verständnis für Optionen haben.)

Fazit: Wenn Sie Swift zum ersten Mal lernen, geben Sie das "!" Charakter ist nicht Teil der Sprache. Es wird dich wahrscheinlich in Schwierigkeiten bringen.

13 Tharzeez Nov 17 2017 at 23:15

Da die obigen Antworten klar erklären, wie man sicher mit Optionals spielt. Ich werde versuchen zu erklären, was Optionals wirklich schnell sind.

Eine andere Möglichkeit, eine optionale Variable zu deklarieren, ist

var i : Optional<Int>

Und der optionale Typ ist nichts anderes als eine Aufzählung mit zwei Fällen, dh

 enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none 
    case some(Wrapped)
    .
    .
    .
}

Also, um unserer Variablen 'i' eine Null zuzuweisen. Wir können var i = Optional<Int>.none einen Wert zuweisen oder übergeben, wir werden einen Wert übergeben var i = Optional<Int>.some(28)

Laut Swift ist 'Null' das Fehlen von Wert. Und um eine Instanz zu erstellen, die mit initialisiert wurde, müssen nilwir uns an ein Protokoll halten, das aufgerufen wird ExpressibleByNilLiteralund großartig ist, wenn Sie es erraten haben. Es wird davon abgeraten , nur anderen Typen zu Optionalsentsprechen ExpressibleByNilLiteralund diesen zu entsprechen.

ExpressibleByNilLiteralhat eine einzige Methode namens, init(nilLiteral:)die ein Instace mit nil initialisiert. Normalerweise wird diese Methode nicht aufgerufen. Laut einer schnellen Dokumentation wird davon abgeraten, diesen Initialisierer direkt aufzurufen, wenn der Compiler ihn aufruft, wenn Sie einen optionalen Typ mit nilLiteral initialisieren .

Sogar ich muss meinen Kopf um Optionals wickeln (kein Wortspiel beabsichtigt): D Happy Swfting All .

12 QiunCheng Jul 30 2016 at 14:18

Zunächst sollten Sie wissen, was ein optionaler Wert ist. Weitere Informationen finden Sie in der Programmiersprache Swift .

Zweitens sollten Sie wissen, dass der optionale Wert zwei Status hat. Einer ist der volle Wert und der andere ist ein Nullwert. Bevor Sie einen optionalen Wert implementieren, sollten Sie überprüfen, um welchen Status es sich handelt.

Sie können if let ...oder guard let ... elseund so weiter verwenden.

Wenn Sie den Variablenstatus vor Ihrer Implementierung nicht überprüfen möchten, können Sie ihn auch verwenden var buildingName = buildingName ?? "buildingName".

8 Wissa Apr 13 2018 at 21:06

Ich hatte diesen Fehler einmal, als ich versuchte, meine Outlets-Werte aus der Methode "Vorbereitung auf Segue" wie folgt festzulegen:

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
        }
    }
}

Dann stellte ich fest, dass ich die Werte der Ziel-Controller-Ausgänge nicht einstellen kann, da der Controller noch nicht geladen oder initialisiert wurde.

Also habe ich es so gelöst:

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)
        }
    }
}

Ziel-Controller:

// 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
}

Ich hoffe, diese Antwort hilft jedem da draußen mit dem gleichen Problem, bei dem ich festgestellt habe, dass die markierte Antwort eine großartige Ressource für das Verständnis der Optionen und ihrer Funktionsweise ist, aber das Problem selbst nicht direkt angesprochen hat.

7 Cristik Sep 19 2018 at 13:04

Grundsätzlich haben Sie versucht, einen Nullwert an Stellen zu verwenden, an denen Swift nur Nicht-Nullwerte zulässt, indem Sie dem Compiler mitgeteilt haben, dass er Ihnen vertrauen soll, dass dort niemals ein Nullwert vorhanden sein wird, sodass Ihre App kompiliert werden kann.

Es gibt verschiedene Szenarien, die zu dieser Art von schwerwiegenden Fehlern führen:

  1. Zwangsauspacken:

    let user = someVariable!
    

    Wenn someVariableNull ist, dann bekommen Sie einen Absturz. Durch das erzwungene Entpacken haben Sie die Verantwortung für die Nullprüfung vom Compiler auf Sie verlagert. Durch das erzwungene Entpacken garantieren Sie dem Compiler, dass Sie dort niemals Nullwerte haben. Und raten Sie mal, was passiert, wenn ein Nullwert irgendwie endet someVariable?

    Lösung? Verwenden Sie die optionale Bindung (auch bekannt als if-let) und führen Sie dort die Variablenverarbeitung durch:

    if user = someVariable {
        // do your stuff
    }
    
  2. Zwangsabgüsse:

    let myRectangle = someShape as! Rectangle
    

    Hier weisen Sie den Compiler durch Force Casting an, sich keine Sorgen mehr zu machen, da Sie dort immer eine RectangleInstanz haben. Und solange das so ist, müssen Sie sich keine Sorgen machen. Die Probleme beginnen, wenn Sie oder Ihre Kollegen aus dem Projekt anfangen, Nicht-Rechteck-Werte zu verteilen.

    Lösung? Verwenden Sie die optionale Bindung (auch bekannt als if-let) und führen Sie dort die Variablenverarbeitung durch:

    if let myRectangle = someShape as? Rectangle {
        // yay, I have a rectangle
    }
    
  3. Implizit ausgepackte Optionen. Angenommen, Sie haben die folgende Klassendefinition:

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

    Wenn nun niemand die nameEigenschaft durcheinander bringt, indem er sie auf setzt nil, funktioniert sie wie erwartet. Wenn sie Userjedoch von einem JSON initialisiert wird, dem der nameSchlüssel fehlt , wird beim Versuch, die Eigenschaft zu verwenden, der schwerwiegende Fehler angezeigt.

    Lösung? Verwenden Sie sie nicht :) Es sei denn, Sie sind zu 102% sicher, dass die Eigenschaft zum Zeitpunkt ihrer Verwendung immer einen Wert ungleich Null hat. In den meisten Fällen funktioniert die Konvertierung in eine optionale oder nicht optionale Version. Wenn Sie es nicht optional machen, hilft Ihnen der Compiler auch dabei, indem Sie den Codepfaden mitteilen, die Sie verpasst haben, und dieser Eigenschaft einen Wert geben

  4. Nicht angeschlossene oder noch nicht angeschlossene Steckdosen. Dies ist ein besonderer Fall von Szenario 3. Grundsätzlich haben Sie eine XIB-geladene Klasse, die Sie verwenden möchten.

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

    Wenn Sie nun die Verbindung zum Outlet über den XIB-Editor verpasst haben, stürzt die App ab, sobald Sie den Outlet verwenden möchten. Lösung? Stellen Sie sicher, dass alle Steckdosen angeschlossen sind. Oder verwenden Sie den ?Operator für sie : emailTextField?.text = "[email protected]". Oder deklarieren Sie den Ausgang als optional. In diesem Fall zwingt Sie der Compiler jedoch, den gesamten Code zu entpacken.

  5. Werte, die von Objective-C stammen und keine Anmerkungen zur Nullbarkeit enthalten. Nehmen wir an, wir haben die folgende Objective-C-Klasse:

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

    Wenn nun keine Annullierungsanmerkungen angegeben werden (entweder explizit oder über NS_ASSUME_NONNULL_BEGIN/ NS_ASSUME_NONNULL_END), wird die nameEigenschaft in Swift als String!(eine IUO - implizit entpackt optional) importiert . Sobald ein schneller Code den Wert verwenden möchte, stürzt er ab, wenn er nameNull ist.

    Lösung? Fügen Sie Ihrem Objective-C-Code Anmerkungen zur Nullbarkeit hinzu. Beachten Sie jedoch, dass der Objective-C-Compiler in Bezug auf die Nullbarkeit ein wenig freizügig ist. Möglicherweise erhalten Sie keine Werte, selbst wenn Sie diese explizit als markiert haben nonnull.

2 Honey Nov 01 2018 at 11:39

Dies ist eher ein wichtiger Kommentar und deshalb können implizit entpackte Optionen beim Debuggen von nilWerten täuschen .

Denken Sie an den folgenden Code: Er wird ohne Fehler / Warnungen kompiliert:

c1.address.city = c3.address.city

Zur Laufzeit wird jedoch der folgende Fehler ausgegeben : Schwerwiegender Fehler: Beim Auspacken eines optionalen Werts wurde unerwartet Null gefunden

Kannst du mir sagen, welches Objekt ist nil?

Das kannst du nicht!

Der vollständige Code wäre:

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
}

Kurz gesagt, wenn var address : Address!Sie verwenden, verbergen Sie die Möglichkeit, dass eine Variable nilvon anderen Lesern stammen kann. Und wenn es abstürzt, sagst du: "Was zum Teufel?! Mein addressist nicht optional. Warum stürze ich dann ab?!.

Daher ist es besser, als solches zu schreiben:

c1.address.city = c2.address!.city  // ERROR:  Fatal error: Unexpectedly found nil while unwrapping an Optional value 

Können Sie mir jetzt sagen, welches Objekt es war nil?

Diesmal wurde Ihnen der Code klarer gemacht. Sie können rationalisieren und denken, dass es wahrscheinlich der addressParameter ist, der gewaltsam entpackt wurde.

Der vollständige Code wäre:

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 AleMohamad May 21 2019 at 00:34

Die Fehler EXC_BAD_INSTRUCTIONund fatal error: unexpectedly found nil while implicitly unwrapping an Optional valueerscheinen am häufigsten, wenn Sie eine deklariert haben @IBOutlet, aber nicht mit dem Storyboard verbunden sind .

Sie sollten auch lernen, wie Optionals funktionieren, wie in anderen Antworten erwähnt, aber dies ist das einzige Mal, das mir am häufigsten erscheint.

2 ShigaSuresh Feb 13 2020 at 01:16

Wenn dieser Fehler in CollectionView auftritt, versuchen Sie, auch eine CustomCell-Datei und Custom xib zu erstellen.

Fügen Sie diesen Code in ViewDidLoad () bei mainVC hinzu.

    let nib = UINib(nibName: "CustomnibName", bundle: nil)
    self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
MikeVolmar Sep 26 2019 at 22:11

Ich bin auf diesen Fehler gestoßen, als ich von einem Tabellenansichts-Controller zu einem Ansichts-Controller gewechselt bin, weil ich vergessen hatte, den benutzerdefinierten Klassennamen für den Ansichts-Controller im Haupt-Storyboard anzugeben.

Etwas Einfaches, das es wert ist, überprüft zu werden, ob alles andere in Ordnung aussieht