1. ホーム
  2. スクリプト・コラム
  3. その他

[解決済み】「Fatal error: Unexpectedly found nil while unwrapping an Optional value "とはどういう意味ですか?

2022-02-07 16:48:53

質問

Swiftのプログラムを書いたのですが、実行すると EXC_BAD_INSTRUCTION というエラーが表示されます。

Fatal error: Unexpectedly found nil while unwrapping an Optional value

も同様のエラーです。

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value


この投稿は、"予期せず nil" が見つかった問題に対する回答を収集することを目的としており、回答が散在して見つけにくくならないようにするためです。ご自由にご自身の答えを追加してください。 編集 既存のWikiの回答

解決方法は?

<上 この回答は コミュニティWiki . もし、より良いものになると思われるのであれば、遠慮なく 編集する !

背景 オプショナルとは?

Swiftでは Optional<Wrapped> オプションタイプ この型は、元の型("Wrapped")から任意の値を含むことができ、また全く値を含まないこともできます(特殊な値である nil ). オプションの値は アンラップド を使用することができます。

オプショナルは 汎用タイプ ということになります。 Optional<Int>Optional<String> の中の型は、異なる型です。 <> をWrapped型と呼びます。オプションは、その内部では 列挙 の2つのケースを持つ。 .some(Wrapped).none ここで .none は、以下のものと同等です。 nil .

オプショナルは、名前付き型を使って宣言することができます。 Optional<T> または、(最も一般的な)略記法として ? というサフィックスをつける。

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

オプショナルは、コードを書きながら自分の仮定を表現するためのシンプルかつ強力なツールです。コンパイラはこの情報を使って、あなたが間違いを犯さないようにすることができます。以下より プログラミング言語「Swift :

<ブロッククオート

Swiftは タイプセーフ つまり、コードが扱うことのできる値の種類を明確にすることができる言語なのです。もしあなたのコードの一部が String を渡すと、型安全性により Int を誤って 同様に、型安全性により、誤ってオプションの String を必要とするコードに、オプションでない String . 型安全性は、開発プロセスのできるだけ早い段階でエラーを発見し、修正するのに役立ちます。

他のプログラミング言語にも、汎用的な オプションタイプ : 例えば たぶん をHaskellで表現しています。 オプション はRustで、そして オプショナル C++17では

プログラミング言語では なし オプションタイプでは、特定の センチネル値 は、有効な値がないことを示すためによく使われる。Objective-Cでは、例えば。 nil (その ヌルポインター ) は、オブジェクトの欠如を表します。のような原始的な型では int NULLポインターは使えないので、別の変数(例えば value: IntisValid: Bool ) または指定されたセンチネル値 (例えば -1 または INT_MIN ). このような方法では isValid またはセンチネル値をチェックする必要があります。また、特定の値がセンチネルとして選択された場合、それはもはや 有効な の値です。

のようなオプションタイプは、Swiftの Optional はこれらの問題を解決するために、特別な別の nil また、強い型システムを利用して、必要なときに nil をチェックすることをコンパイラが覚えてくれるようにします。


なぜ、" 致命的なエラーです。Optional 値のアンラップ中に予期せず nil が見つかりました。 "?

オプショナルの値にアクセスするには (値がある場合)、次のようにする必要があります。 アンラップ です。オプショナルな値は、安全に、あるいは強制的にアンラップすることができます。もしオプショナルを強制的にアンラップして はなかった が値を持つと、上記のようなメッセージが表示され、プログラムがクラッシュします。

Xcodeは、コードの行をハイライトすることでクラッシュを表示します。問題はこの行で発生します。

このクラッシュは、2種類のforce-unwrapで発生する可能性があります。

1. 明示的なフォースアラップ

これを行うには ! 演算子をオプションで使用します。例えば

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

致命的なエラーです。Optional値のアンラップ中に予期せずnilが見つかりました。

として anOptionalStringnil で、強制的にアンラップした行でクラッシュします。

2. 暗黙のうちにアンラップされるオプショナルツール

これらは ! ではなく ? の後に、型が表示されます。

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

これらのオプショナルは、値を含むことが前提となっています。したがって、暗黙的にアンラップされたオプショナルにアクセスするときはいつでも、それは自動的に強制的にアンラップされることになります。もし、値が含まれていなければ、クラッシュします。

print(optionalDouble) // <- CRASH

致命的なエラーです。予期しない nil が見つかりました。 暗黙のうちに Optional 値のアンラップ

どの変数がクラッシュを引き起こしたかを調べるには、以下のようにします。 をクリックすると定義が表示され、そこにオプションの型が表示されるかもしれません。

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


Optionalの強制アンラップはどのような場合に行うべきですか?

明示的な強制アンラップ

一般的なルールとして、オプショナルを強制的にアンラップするために ! 演算子を使用します。を使用する場合があります。 ! しかし、このオプションが値を含んでいることが100%確実な場合にのみ使用すべきです。

がある一方で かもしれません。 のように、強制アンラップが使える場合があります。 事実 オプショナルに値が含まれていること、つまり シングル その代わりに、そのオプショナルを安全にアンラップできない場所です。

暗黙のうちにアンラップされるオプショナルツール

これらの変数は、コードの後半まで代入を先延ばしできるように設計されています。それは あなたの アクセスする前に、値があることを確認する責任があります。しかし、これらはフォース・アンラップを含むため、本質的に安全ではありません。 を想定しています。 nilを代入することは有効ですが、その値はnil以外です。

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

ただし 暗黙のうちにアンラップされたオプショナルが有益であるいくつかのシナリオ また、以下のように安全にアンラップするための様々な方法を使用することができます。 常に 十分な注意を払いながら使用してください。


Optionalを安全に扱うには?

オプショナルに値が含まれているかどうかを確認する最も簡単な方法は、その値を 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 {...} バインドした後に新しい変数の値を変更する必要があるかどうかによります。

例えば

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

もう一つの巧妙なトリックは、ラップを解いた後、カンマを使って値に対してある条件をチェックすることもできる。

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

if文の中でオプショナルバインディングを使うと、ラップされていない値にはその文の範囲内からしかアクセスできないのが唯一の欠点です。もしステートメントのスコープ外から値にアクセスする必要がある場合は、if文の中で ガード文 .

A ガードステートメント この条件を満たす場合にのみ、現在のスコープが実行を継続します。これは次の構文で定義します。 guard condition else {...} .

そこで、オプショナルバインディングで使用するには、次のようにします。

guard let number = anOptionalInt else {
    return
}

(なお、ガード本体内では のいずれかを使用します。 制御伝達文 現在実行中のコードのスコープを抜けるため)。

もし anOptionalInt が値を含んでいる場合、その値はラップされずに新しい number の定数です。コード の場合、ガードの実行は継続されます。もし値が含まれていなければ、ガードは括弧内のコードを実行し、制御の移行を行い、直後のコードは実行されません。

ガード文の優れた点は、ラップされていない値を、その文に続くコードで使用できることです。 のみ が値を持つ場合に実行されます)。これは '運命のピラミッド' 複数のif文のネストによって発生します。

例えば

guard let number = anOptionalInt else {
    return
}

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

ガードは、if 文がサポートしていたのと同じ巧妙なトリックもサポートしています。 where 節があります。

if文とguard文のどちらを使用するかは、将来のコードが が必要です。 が値を含んでいることを確認します。

Nil Coalescing オペレータ

Nil Coalescing Operator(ニル・コアレスティング・オペレーター の便利な略記版です。 三項条件演算子 主にオプショナルをノン・オプショナルに変換するために設計されています。これは,次のような構文になります。 a ?? b ここで a はオプションの型であり b と同じ型です。 a (通常は非オプショナルですが)。

これは、基本的に「もし a が値を含んでいる場合、それをアンラップします。そうでない場合は b 代わりに "です。例えば、こんな使い方ができます。

let number = anOptionalInt ?? 0

これは number の定数 Int 型の値を含む。 anOptionalInt 値を含む場合、または 0 を指定します。

の略語に過ぎない。

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

オプショナル・チェイニング

を使用することができます。 オプショナル・チェーニング を使用すると、オプショナルメソッドを呼び出したりプロパティにアクセスしたりすることができます。これは、変数名の末尾に ? を使用します。

例えば、変数 foo で、タイプはオプションの Foo のインスタンスです。

var foo : Foo?

のメソッドを呼び出したい場合 foo 何も返さないのであれば、単純にそうすることができます。

foo?.doSomethingInteresting()

もし foo が値を含んでいれば、その値に対してこのメソッドが呼ばれます。もし値がなければ、何も悪いことは起こらず、コードは単に実行を継続します。

(にメッセージを送信するのと同様の動作です)。 nil Objective-Cの場合)

したがって、これはメソッドを呼び出すだけでなく、プロパティを設定するためにも使用することができます。例えば

foo?.bar = Bar()

ここでも、以下のようにすれば、何も悪いことは起こりません。 foonil . あなたのコードは単に実行し続けるだけです。

オプショナルチェーンでできるもうひとつの技は、プロパティの設定やメソッドの呼び出しがうまくいったかどうかをチェックすることです。これを行うには、返り値を nil .

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

例えば

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

しかし、プロパティにアクセスしたり、値を返すメソッドを呼び出したりしようとすると、少しやっかいなことになります。なぜなら foo はオプショナルなので、そこから返されるものもオプショナルになります。これに対処するには、上記の方法のいずれかを使って返されるオプショナルをアンラップするか、あるいは foo にアクセスしたり、値を返すメソッドを呼び出したりする前に、そのメソッド自体を削除してください。

また、その名の通り、これらの文は「連鎖」させることができます。つまり、もし foo は、オプションのプロパティ baz というプロパティがあります。 qux - と書くと、次のようになります。

let optionalQux = foo?.baz?.qux

ここでも、なぜなら foobaz はオプションで qux に関係なく、常にオプショナルになります。 qux 自体がオプションである。

mapflatMap

オプショナルでよく使われない機能として mapflatMap 関数があります。これらは、オプショナルな変数にオプショナルではない変換を適用するためのものです。オプショナル変数が値を持つ場合、指定した変換を適用することができます。値を持たない場合は、そのまま nil .

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

let anOptionalString:String?

を適用することで map 関数を使用することができます。 stringByAppendingString 関数を使って、別の文字列に連結することができます。

なぜなら stringByAppendingString は非オプションの文字列を引数に取るので、オプションの文字列を直接入力することはできません。しかし map を使用すると、allow 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 {
    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のエラー処理システムは、quot;force try"する方法も提供しています。 try! :

let result = try! someThrowingFunc()

この記事で説明したコンセプトはここでも適用されます。エラーがスローされると、アプリケーションはクラッシュします。

のみを使用する必要があります。 try! その結果があなたのコンテキストで決して失敗しないことを証明できる場合 - そしてこれは非常に稀なことです。

ほとんどの場合、完全なDo-Try-Catchシステム、およびオプションのシステムを使用することになるでしょう。 try? を、エラーの処理が重要でない稀なケースで使用します。


リソース