「致命的エラー:オプション値のアンラップ中に予期せずnilが見つかりました」とはどういう意味ですか?


415

SwiftプログラムがクラッシュしEXC_BAD_INSTRUCTION、次のようなエラーのいずれかが発生します。このエラーはどういう意味ですか、どうすれば修正できますか?

致命的なエラー:オプション値のアンラップ中に予期せずnilが見つかりました

または

致命的エラー:オプション値を暗黙的にアンラップしているときに予期せずnilが見つかりました


この投稿は、「予期せず発見されたnil」問題への回答を収集することを目的としています。そのため、それらは散らばり、見つけるのが難しくありません。独自の回答を追加するか、既存のWiki回答を編集してください。


8
@RobertColumbia、これは問題に関する広範なドキュメントを備えたコミュニティWikiのQ&Aペアです。MCVEがないためにこの質問を閉じるべきではないと思います。
JAL

次のような変数を作成します[var nameOfDaughter:String?]そして、この変数を次のように使用します[if let _ = nameOfDaughter {}]が最善のアプローチです。
iOS

回答:


673

この回答はコミュニティWikiです。改善できると思われる場合は、自由に編集してください。

背景:オプションとは何ですか?

Swiftでは、Optionalは(任意の種類の)値を含むか、まったく値を含まないジェネリック型です。

他の多くのプログラミング言語では、特定の「センチネル」値が値の欠如を示すためによく使用されます。たとえば、Objective-Cでは、nilnullポインター)はオブジェクトがないことを示します。しかし、これはプリミティブ型を操作するときにさらにトリッキーになります。-1整数、またはINT_MIN、またはその他の整数がないことを示すために使用する必要がありますか?「整数なし」を意味する特定の値が選択された場合、それは有効な値として処理できなくなったことを意味します。

Swiftはタイプセーフな言語です。つまり、この言語は、コードで処理できる値のタイプを明確にするのに役立ちます。コードの一部に文字列が必要な場合、タイプセーフにより、誤ってIntを渡さないようにすることができます。

Swiftでは、どのタイプもオプションにすることができます。オプションの値は、元のタイプの任意の値、または特別な値を取ることができますnil

オプションは?、タイプのサフィックスで定義されます。

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

オプションに値がないことは、次のように示されnilます。

anOptionalInt = nil

(これnilnilObjective-C と同じではないことに注意してください。Objective-Cではnil、有効なオブジェクトポインターがありません。Swiftでは、Optionalはオブジェクト/参照タイプに制限されていません。OptionalはHaskellのMaybeと同様に動作します。)


なぜ「致命的なエラー:オプション値のアンラップ中に予期せずnilが見つかりました」が表示されたのですか?

オプションの値にアクセスするには(値がある場合)、ラップ解除する必要があります。オプションの値は、安全または強制的にアンラップできます。オプションを強制アンラップし、それに値がない場合、プログラムは上記のメッセージでクラッシュします。

Xcodeはコード行を強調表示することでクラッシュを表示します。この行で問題が発生。

クラッシュしたライン

このクラッシュは、2種類の強制アンラップで発生する可能性があります。

1.明示的な強制アンラップ

これは!、オプションの演算子で行われます。例えば:

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

致命的なエラー:オプション値のアンラップ中に予期せずnilが見つかりました

以下のようanOptionalStringであるnilここで、あなたはそれアンラップ強制ライン上でクラッシュを取得します。

2.暗黙的にアンラップされたオプション

これらは、タイプの後にでは!なく、で定義され?ます。

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

これらのオプションには値が含まれていると想定されます。したがって、暗黙的にラップ解除されたオプションにアクセスすると、自動的に強制的にラップ解除されます。値が含まれていない場合、クラッシュします。

print(optionalDouble) // <- CRASH

致命的エラー:オプション値を暗黙的にアンラップしているときに予期せずnilが見つかりました

クラッシュの原因となった変数を特定するには、押したままクリックして定義を表示します。ここで、オプションのタイプを見つけることができます。

特に、IBOutletsは通常、暗黙的にアンラップされたオプションです。これは、初期化、実行時にxibまたはストーリーボードがアウトレットをリンクするためです。したがって、コンセントがロードされる前にアウトレットにアクセスしていないことを確認する必要があります。また、ストーリーボード/ xibファイルで接続が正しいことを確認する必要があります。そうでない場合、値はnil実行時になり、暗黙的にアンラップされるとクラッシュします。 。接続を修正するときは、アウトレットを定義するコード行を削除してから、再接続してください。


いつオプションを強制的にアンラップする必要がありますか?

明示的な強制アンラップ

一般的な規則として、!演算子を使用してオプションを明示的に強制的にアンラップしないでください。使用!が許容される場合もありますが、オプションに値が含まれていることを100%確信している場合にのみ使用してください。

そこにいる間、あなたが知っているように、あなたはアンラップ力を使用することができる機会があり、実際のオプションに値が含まれていること-がない、単一のオプションの代わりに、あなたが安全にアンラップすることができない場所。

暗黙的にアンラップされたオプション

これらの変数は、コードの後半までそれらの割り当てを延期できるように設計されています。あなたがそれらにアクセスする前にそれらが価値を持っていることを確認することはあなたの責任です。ただし、強制アンラップが含まれるため、nilの割り当てが有効であっても、値が非nilであると想定しているため、本質的に安全ではありません。

最後の手段として、暗黙的にアンラップされたオプションを使用する必要があります。遅延変数を使用できる場合、または変数デフォルト値を提供できる場合は、暗黙的にラップされていないオプションを使用する代わりに、そうする必要があります。

ただし、暗黙的にラップ解除されたオプションが有益であるシナリオがいくつかあり、以下にリストされているようにそれらを安全にラップ解除するさまざまな方法を使用できますが、常に十分に注意して使用する必要があります。


オプションを安全に処理するにはどうすればよいですか?

オプションに値が含まれているかどうかを確認する最も簡単な方法は、と比較することnilです。

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

ただし、オプションを使用する場合は、99.9%の時間で、実際に値が含まれている場合は、その値にアクセスする必要があります。これを行うには、オプションのバインドを使用できます。

オプションのバインディング

オプションのバインドを使用すると、オプションに値が含まれているかどうかを確認でき、ラップされていない値を新しい変数または定数に割り当てることができます。バインド後に新しい変数の値を変更する必要があるかどうif let x = anOptional {...}if var x = anOptional {...}に応じて、構文orを使用します。

例えば:

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

これが行うことは、最初にオプションに値が含まれていることを確認することです。それは場合に行い、その後、「開封された」の値は、新しい変数(に割り当てられているnumber、それは非オプションであるかのようにあなたが自由に使用することができます- )。オプションに値が含まれていない場合、予想どおり、else句が呼び出されます。

オプションのバインディングの優れた点は、複数のオプションを同時にアンラップできることです。ステートメントをコンマで区切ることができます。すべてのオプションがアンラップされた場合、ステートメントは成功します。

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

もう1つの巧妙なトリックは、値をアンラップした後、コンマを使用して値の特定の条件をチェックできることです。

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

ifステートメント内でオプションのバインディングを使用する唯一の問題は、ステートメントのスコープ内からのみラップされていない値にアクセスできることです。ステートメントのスコープ外から値にアクセスする必要がある場合は、ガードステートメントを使用できます。

ガード文はあなたが成功するための条件を定義することができます-そしてその条件が満たされた場合、現在のスコープにのみ実行を継続します。それらは構文で定義されますguard condition else {...}

したがって、オプションのバインディングでそれらを使用するには、次のようにします。

guard let number = anOptionalInt else {
    return
}

(ガード本文内では、現在実行中のコードのスコープを終了するために、制御転送ステートメントの 1つを使用する必要があることに注意してください)。

場合はanOptionalInt値が含まれ、それが開封され、新しいに割り当てられますnumber定数。その後、ガードののコードは実行を継続します。値が含まれていない場合、ガードは括弧内のコードを実行します。これにより、制御が移るため、直後のコードは実行されません。

ガードステートメントの本当の素晴らしいところは、ラップされていない値がステートメントに続くコードで使用できるようになったことです(将来のコードはオプションが値を持つ場合にのみ実行できることがわかっているため)。これは、複数のifステートメントをネストすることによって作成される「運命のピラミッド」を排除するのに最適です。

例えば:

guard let number = anOptionalInt else {
    return
}

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

ガードは、同時に複数のオプションをラップ解除してwhere句を使用するなど、ifステートメントがサポートするのと同じ巧妙なトリックもサポートします。

ifステートメントまたはguardステートメントのどちらを使用するかは、将来のコードで値にオプションを含める必要があるかどうかに完全に依存します。

ニル合体オペレーター

無記号合体演算子の気の利いた簡略版である三条件演算子主として非optionalsとoptionalsを変換するために設計されました、。構文a ?? baあり、はオプションのタイプでbあり、と同じタイプですa(ただし、通常はオプションではありません)。

本質的にaは、「値が含まれている場合は、ラップを解除します。そうでない場合は、b代わりに戻ります。」たとえば、次のように使用できます。

let number = anOptionalInt ?? 0

これはnumberInt型の定数を定義します。これには、の値がanOptionalInt含まれている場合、値が含まれている場合、または0その他の値が含まれます。

これは以下の略記です:

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

オプションの連鎖

オプションのチェーンを使用して、メソッドを呼び出したり、オプションのプロパティにアクセスしたりできます。これは、変数名を?使用するときに、変数名にaを付けることで簡単に行えます。

たとえばfoo、オプションのFooインスタンスタイプの変数があるとします。

var foo : Foo?

foo何も返さないメソッドを呼び出したい場合は、次のようにするだけです。

foo?.doSomethingInteresting()

場合はfoo値が含まれ、この方法は、それに呼び出されます。そうでない場合、問題は何も起こりません。コードは単に実行を継続します。

(これはnilObjective-Cでのメッセージの送信と同様の動作です)

したがって、これはプロパティの設定やメソッドの呼び出しにも使用できます。例えば:

foo?.bar = Bar()

繰り返しになりますが、ここで問題fooはありませんnil。コードは単に実行を続けます。

オプションのチェーンで実行できるもう1つの巧妙なトリックは、プロパティの設定またはメソッドの呼び出しが成功したかどうかを確認することです。これを行うには、戻り値をと比較しnilます。

(これは、何も返さないメソッドではVoid?なくオプションの値が返されるためですVoid

例えば:

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

ただし、プロパティにアクセスしたり、値を返すメソッドを呼び出したりしようとすると、状況が少しトリッキーになります。fooはオプションなので、そこから返されるものもすべてオプションになります。これに対処するには、上記のメソッドのいずれかを使用して返されるオプションをアンラップするか、メソッドfooにアクセスするか、値を返すメソッドを呼び出す前に、それ自体をアンラップします。

また、名前が示すように、これらのステートメントを一緒に「チェーン」することができます。これは、がプロパティを持つfooオプションのプロパティを持っている場合、次のように記述できることを意味します。bazqux

let optionalQux = foo?.baz?.qux

ここでも、fooおよびbazはオプションであるため、から返される値は、それ自体がオプションquxかどうかに関係なく、常にquxオプションです。

map そして flatMap

オプションでよく使用さmapれないflatMap機能は、および機能を使用する機能です。これらにより、オプション変数に非オプション変換を適用できます。オプションに値がある場合、それに任意の変換を適用できます。値がない場合はそのままになりますnil

たとえば、オプションの文字列があるとします。

let anOptionalString:String?

map関数をそれに適用することにより、stringByAppendingString別の文字列に連結するために関数を使用できます。

stringByAppendingStringオプションの文字列引数を取るため、オプションの文字列を直接入力することはできません。ただし、mapを使用するstringByAppendingStringことでanOptionalString、が値を持っている場合に使用できるようにすることができます。

例えば:

var anOptionalString:String? = "bar"

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

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

ただし、anOptionalString値がない場合はmapを返しnilます。例えば:

var anOptionalString:String?

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

print(anOptionalString) // nil

flatMapと同様に機能しますが、クロージャー本体内から別のオプションmapを返すことができます。つまり、オプションではない入力が必要なプロセスにオプションを入力できますが、オプション自体を出力できます。

try!

Swiftのエラー処理システムは、Do-Try-Catchで安全に使用できます。

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

someThrowingFunc()エラーがスローされた場合、エラーはcatchブロックで安全にキャッチされます。

errorあなたが見一定のcatchブロックは、当社が宣言されていない-それは自動的にによって生成されますcatch

error自分で宣言することもできます。たとえば、次のように、有用な形式にキャストできるという利点があります。

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

tryこの方法を使用することは、関数をスローすることから発生するエラーを試し、キャッチし、処理する適切な方法です。

try?エラーを吸収するものもあります:

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

しかし、Swiftのエラー処理システムは、次のように「強制的に試行」する方法も提供しますtry!

let result = try! someThrowingFunc()

この投稿で説明されている概念はここにも当てはまります。エラーがスローされると、アプリケーションがクラッシュします。

try!その結果がコンテキスト内で失敗しないことを証明できる場合にのみ使用してください-これは非常にまれです。

ほとんどの場合、完全なDo-Try-Catchシステム、およびオプションのシステムを使用します。try?まれなケースでは、エラーの処理が重要ではありません。


資源


87
個人的には、これらすべてを書く努力をしてくださったことに感謝したいと思います。これは、初心者にもエキスパートにも、swiftを使用する際に役立つと思います。
Pranav Wadhwa

15
お役に立ててうれしいです。この回答はコミュニティWikiなので、多くの人々(これまでのところ7人)間のコラボレーションです!
jtbandes

1
私の場合、このエラーは断続的です。助言がありますか?stackoverflow.com/questions/50933681/…の
py_ios_dev

1
おそらく、この回答をのcompactMap()代わりに使用するように更新する時がきましたflatMap()
Nicolas Miari

だから私は最善かつ短い解決策は「ニル結合演算子」だと思いますよね?
mehdigriche

65

TL; DR回答

非常に少数の例外、このルールは黄金です。

の使用を避ける !

?暗黙的にアンラップされたオプション(IUO)(!)ではなく、変数オプション()を宣言する

つまり、次のように使用します。
var nameOfDaughter: String?

の代わりに:
var nameOfDaughter: String!

if letまたはを使用してオプション変数をアンラップしますguard let

次のように変数をアンラップします。

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

またはこのように:

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

この回答は簡潔にすることを目的としており、完全に理解できるように、回答は承認されました。


資源


40

この質問はいつも出てきますます。これは、新しいSwift開発者が最初に苦労することの1つです。

バックグラウンド:

Swiftは「オプション」の概念を使用して、値を含むか含まない値を処理します。Cのような他の言語では、変数に値0を格納して、値が含まれていないことを示す場合があります。ただし、0が有効な値である場合はどうなりますか?次に、-1を使用します。-1が有効な値である場合はどうなりますか?等々。

Swiftのオプションを使用すると、任意のタイプの変数を設定して、有効な値を含めるか、値を含めないようにすることができます。

変数を意味するように宣言するときは、タイプの後に疑問符を付けます(タイプx、または値なし)。

オプションは、実際には、指定されたタイプの変数を含むか、何も含まないコンテナです。

内部の値をフェッチするには、オプションを「ラップ解除」する必要があります。

「!」演算子は「強制アンラップ」演算子です。それは「私を信頼してください。私は自分が何をしているかを知っています。このコードが実行されたときに、変数にnilが含まれないことを保証します。」間違っていると、クラッシュします。

あなたが本当にない限りないあなたは何をしているかを知って、「!」を避けます アンラップ演算子を強制します。これはおそらく、Swiftプログラマの初心者にとってクラッシュの最大の原因です。

オプションの扱い方:

より安全なオプションを処理する方法は他にもたくさんあります。ここにいくつかあります(完全なリストではありません)

「オプションのバインディング」または「if let」を使用して、「このオプションに値が含まれている場合は、その値を新しい非オプション変数に保存します。オプションに値が含まれていない場合は、このifステートメントの本文をスキップしてください」

オプションのバインディングの例を次に示しfooます。

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

オプションの入札を使用するときに定義する変数は、ifステートメントの本文にのみ存在します(「スコープ内のみ」)。

または、変数がnilの場合に関数を終了できるガードステートメントを使用することもできます。

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

GuardステートメントはSwift 2で追加されました。Guardを使用すると、コード全体の「ゴールデンパス」を維持し、オプションの「if let」バインディングを使用した結果としてネストされたifのレベルが増加するのを防ぐことができます。

「nil合体演算子」と呼ばれる構造もあります。「optional_var ?? replacement_val」の形式を取ります。これは、オプションに含まれるデータと同じタイプの非オプション変数を返します。オプションにnilが含まれている場合、「??」の後の式の値を返します シンボル。

したがって、次のようなコードを使用できます。

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

また、try / catchまたはガードのエラー処理を使用することもできますが、通常は上記の他の手法の1つがよりクリーンです。

編集:

オプションを使用したもう少し微妙な問題は、「暗黙的にラップされていないオプションです。fooを宣言すると、次のようになります。

var foo: String!

その場合でもfooはオプションですが、それを参照するためにアンラップする必要はありません。つまり、fooを参照しようとすると、それがnilの場合はクラッシュします。

したがって、このコード:

var foo: String!


let upperFoo = foo.capitalizedString

fooを強制的にアンラップしていない場合でも、fooのCapitalizedStringプロパティへの参照でクラッシュします。プリントはきれいに見えますが、そうではありません。

したがって、暗黙的にアンラップされたオプションについては、本当に注意が必要です。(そして、オプションについてしっかりと理解するまで、それらを完全に避けてください。)

結論:Swiftを初めて学ぶときは、「!」文字は言語の一部ではありません。トラブルに巻き込まれる可能性があります。


7
これを公開Wikiにすることを検討してください。
vacawama 2016

3
回答を編集すると、右側の編集ボックスの下にコミュニティWikiチェックボックスがあります。回答の担当者はもういませんが、とにかくこれは露骨な担当者ではありません。
vacawama 2016

6
この質問がサイトで何百万回も尋ねられることを考えると、質問への標準的な回答として自己回答型の質問を投稿するのに時間をかけるつもりなら、答えはこれよりはるかに完全であることを望みます。guard条項については何もありません。の使用がif varと同じように有効な構成であるものについては何もありませんif let。バインドwhereについて話しているときに言及する価値があると思う節については何もありませんif let(多くの場合、ネストのレイヤー全体が削除されます)。オプションのチェーンについては何もありません。
nhgrif 2016

6
また、暗黙的にアンラップされたオプションを言及するとき、前述のオプションのトリックをすべて使用して、暗黙的にアンラップされたオプションを安全にアンラップできることについても言及しません。また、同じ句で複数のオプションをアンラップすることもカバーしていません。
nhgrif 2016

1
@nhgrif、私は「try / catchまたはガードエラー処理を使用することもできますが、通常、上記の他の手法の1つはよりクリーンです」と言いました。あなたは確かにいくつかの良い点を持っています。これはかなり大きなトピックです。狙撃コメントではなく、私がしなかった事柄をカバーするあなた自身の答えを投稿してみませんか そうすれば、質問とその回答がサイトにとってより良い資産になります。
Duncan C

13

上記の回答は、オプションで安全にプレイする方法を明確に説明しているためです。オプションが本当に速いものを説明してみます。

オプションの変数を宣言する別の方法は、

var i : Optional<Int>

そしてOptionalタイプは、2つのケースを持つ列挙体にすぎません。

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

したがって、変数 'i'にnilを割り当てます。できる var i = Optional<Int>.none か、値を割り当てるには、いくつかの値を渡します var i = Optional<Int>.some(28)

迅速によれば、「nil」は価値の欠如です。そして、で初期化されたインスタンスを作成するnilと呼ばれるプロトコルに準拠しなければならない私たちにExpressibleByNilLiteral、あなたはそれを推測した場合、大きなだけOptionalsに準拠ExpressibleByNilLiteralして、他のタイプに適合することはお勧めできません。

ExpressibleByNilLiteralinit(nilLiteral:)インスタンスをnilで初期化する単一のメソッドが呼び出されます。通常、このメソッドを呼び出すことはありません。迅速なドキュメントによれば、nilリテラルでOptional型を初期化するときは常にコンパイラーがこのイニシャライザを呼び出すため、このイニシャライザを直接呼び出すことはお勧めしません。

私でさえ私の頭をOptionals:D Happy Swfting Allに巻き付ける必要があります(しゃれは意図していません) 。


12

まず、オプション値が何であるかを知っておく必要があります。あなたはにステップすることができスウィフトプログラミング言語の詳細について。

次に、オプションの値には2つのステータスがあることを知っておく必要があります。1つは完全な値で、もう1つはnil値です。したがって、オプションの値を実装する前に、それがどの状態であるかを確認する必要があります。

if let ...またはguard let ... elseなどを使用できます。

別の方法として、実装前に変数の状態を確認したくない場合は、var buildingName = buildingName ?? "buildingName"代わりに使用することもできます。


8

次のように、segueメソッドの準備からOutlets値を設定しようとしたときに、一度このエラーが発生しました。

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

その後、コントローラーがまだロードまたは初期化されていないため、宛先コントローラーアウトレットの値を設定できないことがわかりました。

だから私はこのように解決しました:

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

宛先コントローラー:

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

マークされた回答がオプションとそれらがどのように機能するかを理解するための優れたリソースであるが、問題自体に直接対処していないことがわかったので、この回答が同じ問題を抱えているすべての人に役立つことを願っています。


updateView宛先コントローラーで呼び出す場所を言及することを忘れないでください;)
Ahmad F

@AhmadF良い点。ただしupdateViewname変数を使用してをに設定しnameLabel.textているため、この場合は宛先コントローラーでを呼び出す必要はありませんviewDidLoad。しかし、多くの設定を行っている場合は、それを行う別の関数を作成し、viewDidLoad代わりにそれを呼び出す方が確かに良いでしょう。
ウィッサ

7

基本的に、Swiftがnil以外の値のみを許可する場所でnil値を使用しようとしました。コンパイラーにnil値が存在しないことを信頼するように伝え、アプリをコンパイルできるようにします。

この種の致命的なエラーにつながるシナリオはいくつかあります。

  1. 強制アンラップ:

    let user = someVariable!

    someVariableがnilの場合、クラッシュします。強制アンラップを行うことで、nilチェックの責任をコンパイラーからユーザーに移しました。基本的に強制アンラップを行うことで、コンパイラーにnil値が決してないことを保証します。そして、なんとかしてnil値が終わるとsomeVariableどうなるでしょうか?

    解決?オプションのバインディング(別名if-let)を使用して、そこで変数処理を実行します。

    if user = someVariable {
        // do your stuff
    }
  2. 強制(ダウン)キャスト:

    let myRectangle = someShape as! Rectangle

    ここでは、強制キャストにより、常にRectangleインスタンスが存在するため、コンパイラに心配する必要はありません。そしてそれが続く限り、あなたは心配する必要はありません。問題は、プロジェクトのあなたや同僚が長方形以外の値を循環させ始めたときに始まります。

    解決?オプションのバインディング(別名if-let)を使用して、そこで変数処理を実行します。

    if let myRectangle = someShape as? Rectangle {
        // yay, I have a rectangle
    }
  3. 暗黙的にアンラップされたオプション。次のクラス定義があるとします。

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

    これで、nameプロパティをに設定して誰も混乱させない場合nil、期待どおりに機能しUserますが、nameキーがないJSONから初期化すると、プロパティを使用しようとすると致命的なエラーが発生します。

    解決?それらを使用しないでください:)プロパティが使用される必要があるときまでにプロパティが常に非nil値を持つことを102%確信していない場合を除きます。ほとんどの場合、オプションまたは非オプションへの変換が機能します。それを非オプションにすると、コンパイラーは、そのプロパティに値を与えることで見逃したコードパスを伝えることであなたを助けます

  4. 接続されていない、またはまだ接続されていないコンセント。これは、シナリオ#3の特定のケースです。基本的に、使用したいXIBロードクラスがいくつかあります。

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

    ここで、XIBエディターからのコンセントの接続を忘れた場合、そのコンセントを使用する必要があるとすぐにアプリがクラッシュします。解決?すべてのコンセントが接続されていることを確認してください。または、次の?演算子を使用しますemailTextField?.text = "my@email.com"。または、アウトレットをオプションとして宣言しますが、この場合、コンパイラーは、コード全体でアンラップするように強制します。

  5. Objective-Cからの値であり、null可能性アノテーションはありません。次のObjective-Cクラスがあるとします。

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

    null 可能性アノテーションが指定されていない場合(明示的またはNS_ASSUME_NONNULL_BEGIN/ を介してNS_ASSUME_NONNULL_END)、nameプロパティはString!(IUO-暗黙的にアンラップされたオプション)としてSwiftにインポートされます。一部の迅速なコードが値を使用するようになるとすぐに、namenilの場合はクラッシュします。

    解決?null可能性アノテーションをObjective-Cコードに追加します。ただし、null可能性に関してはObjective-Cコンパイラーは少し寛容です。明示的にとしてマークしたとしても、結果はnil値になる可能性がありますnonnull


2

これは、より重要なコメントであり、nil値のデバッグに関して、暗黙的にアンラップされたオプションが不正である理由です。

次のコードについて考えてみます。エラーや警告なしでコンパイルされます。

c1.address.city = c3.address.city

しかし、実行時に次のエラーが発生します:致命的なエラー:オプション値のアンラップ中に予期せずnilが見つかりました

どのオブジェクトnilか教えていただけますか?

できません!

完全なコードは次のようになります。

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
}

変数を他のリーダーから取得する可能性を隠すために、var address : Address!あなたを使用することによって、長いストーリーは短くなります。そして、それがクラッシュするとき、あなたは「一体何なんだ!! 私はオプションではないので、なぜ私はクラッシュするのか?!niladdress

したがって、次のように書くことをお勧めします。

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

それがどのオブジェクトだったnilか教えていただけますか?

今回はコードがより明確になりました。合理化して、おそらくaddress強制的にアンラップされたパラメーターであると考えることができます。

完全なコードは次のようになります。

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

エラーEXC_BAD_INSTRUCTIONとは、fatal error: unexpectedly found nil while implicitly unwrapping an Optional valueあなたが宣言した場合に最も表示されます@IBOutletが、それに接続されていないストーリーボード

他の回答で述べられているように、オプションがどのように機能するかについても学ぶ必要がありますが、これはほとんど私に見える唯一の時間です。


@IBOutletこのエラーの原因に致命的なエラーがあるべきではありません:エラーのオプション値バージョン暗黙的にアンラップしているときに予期せずnilが見つかりましたか?
pkamb

1
それは本当だ。多分私が意味する答えを送って、最初の致命的なエラーメッセージをコピーして貼り付けたときかもしれません。ハミッシュからの答えはこれについて非常に完全に見えます。
エールモハマド

2

CollectionViewでこのエラーが発生した場合は、CustomCellファイルとカスタムxibも作成してください。

このコードをmainVCのViewDidLoad()に追加します。

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

0

メインのストーリーボードでビューコントローラーのカスタムクラス名を指定するのを忘れていたため、テーブルビューコントローラーからビューコントローラーにセグエを作成しているときにこのエラーに遭遇しました。

他のすべてが正常に見えるかどうかを確認する価値がある簡単なもの

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.