私のSwiftプログラムはEXC_BAD_INSTRUCTION
とこのエラーでクラッシュしています。それはどういう意味ですか、そしてどのように修正しますか?
致命的エラー:Optional値の展開中に予期せずnilが見つかりました
この記事は「予期せず見つかった問題」に対する答えを収集することを目的としているため、それらは散在して見つけることが困難ではありません。あなた自身の答えを追加するか、 編集 既存のウィキの答えを追加してください。
この答えは community wiki です。もっと良くできると思うなら、お気軽に 編集してください !
Swiftでは、- Optional
は 汎用タイプ であり、(任意の種類の)値を含めることも、値をまったく含めないこともできます。
他の多くのプログラミング言語では、特定の「センチネル」値を使用して、値の欠如を示します。たとえば、Objective-Cでは、nil
( nullポインター )はオブジェクトがないことを示します。しかし、プリミティブ型を扱う場合、これはよりトリッキーになります— -1
を使用して、整数、またはINT_MIN
、または他の整数がないことを示す必要がありますか? 「整数なし」を意味する特定の値が選択されている場合、それはvalid値として処理できなくなっていることを意味します。
Swiftはタイプセーフな言語です。つまり、この言語を使用すると、コードで使用できる値のタイプを明確にすることができます。コードの一部が文字列を想定している場合、タイプセーフティにより、誤ってIntを渡すことを防ぎます。
Swiftでは、任意のタイプをオプションにすることができます。オプションの値は、元のタイプの任意の値を取ることができます or 特別な値nil
。
オプションは、タイプに?
サフィックスを付けて定義されます。
var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int? // `nil` is the default when no value is provided
オプションに値がないことは、nil
で示されます。
anOptionalInt = nil
(このnil
はObjective-Cのnil
と同じではないことに注意してください。Objective-Cでは、nil
は有効なオブジェクトポインターがないことです。Swiftでは、オプションはオブジェクト/参照タイプ。オプションの動作はHaskellの 多分 と同様です。)
オプションの値(ある場合)にアクセスするには、unwrapする必要があります。オプションの値は、安全にまたは強制的に展開できます。オプションを強制的にアンラップし、did n'tに値がある場合、プログラムは上記のメッセージでクラッシュします。
Xcodeは、コード行を強調表示することでクラッシュを表示します。問題はこの行で発生します。
このクラッシュは、次の2種類の強制アンラップで発生する可能性があります。
これは、オプションの!
演算子で行われます。例えば:
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
ここでanOptionalString
はnil
であるため、強制的にアンラップした行でクラッシュが発生します。
これらは、タイプの後の!
ではなく、?
で定義されます。
var optionalDouble: Double! // this value is implicitly unwrapped wherever it's used
これらのオプションには値が含まれると想定されます。したがって、暗黙的にアンラップされたオプションにアクセスするたびに、自動的にアンラップされます。値が含まれていない場合、クラッシュします。
print(optionalDouble) // <- CRASH
どの変数がクラッシュを引き起こしたかを調べるために、 ⌥ クリックして定義を表示します。ここには、オプションのタイプがあります。
特に、IBOutletsは通常、暗黙的にラップされていないオプションです。これは、実行時にxibまたはストーリーボードがアウトレットをリンクするため、after初期化です。したがって、ロードされる前にアウトレットにアクセスしていないことを確認する必要があります。また、ストーリーボード/ xibファイルで接続が正しいことを確認する必要があります。そうでない場合、実行時に値がnil
になり、暗黙的にクラッシュしますラップ解除。接続を修正するときは、アウトレットを定義するコード行を削除してから再接続してみてください。
一般的なルールとして、!
演算子を使用してオプションを明示的に強制的にアンラップすることは決してしないでください。 !
の使用が許容される場合がありますが、オプションに値が含まれていることを100%確信している場合にのみ使用してください。
オプションに値が含まれているfactのように、forceで強制的にアンラップできる場合がありますが、 single代わりにそのオプションを安全にアンラップできない場所。
これらの変数は、コードの後半まで割り当てを延期できるように設計されています。あなたがそれらにアクセスする前に値を持っていることを保証するのは、yourの責任です。ただし、強制的なアンラッピングを伴うため、本質的に安全ではありません。assumeであるため、nilの割り当ては有効ですが、nilではありません。
暗黙的にラップされていないオプションのみをlast resortとして使用する必要があります。 遅延変数 を使用できる場合、または変数に デフォルト値 を指定できる場合は、暗黙的にアンラップされたオプションを使用する代わりに、そうする必要があります。
ただし、 暗黙的にアンラップされたオプションが有益ないくつかのシナリオ があり、以下にリストされているように安全にアンラップするさまざまな方法を使用できますが、常にalways十分注意して使用してください。
オプションに値が含まれているかどうかを確認する最も簡単な方法は、nil
と比較することです。
if anOptionalInt != nil {
print("Contains a value!")
} else {
print("Doesn’t contain a value.")
}
ただし、オプションを使用する場合、99.9%の時間、実際に含まれている値にアクセスする必要があります(含まれている場合)。これを行うには、Optional Bindingを使用できます。
オプションのバインディングにより、オプションに値が含まれているかどうかを確認できます。また、ラップされていない値を新しい変数または定数に割り当てることができます。バインド後に新しい変数の値を変更する必要があるかどうかに応じて、構文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")
}
これは、まずオプションに値が含まれていることを最初に確認します。 doesの場合、「ラップされていない」値が新しい変数(number
)に割り当てられます。これは、オプションではないかのように自由に使用できます。オプションのdoes n’tに値が含まれていない場合、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ステートメント内でオプションのバインディングを使用する場合の唯一の問題は、ステートメントのスコープ内からのみ、ラップされていない値にアクセスできることです。ステートメントのスコープ外から値にアクセスする必要がある場合は、guardステートメントを使用できます。
guard statement を使用すると、成功の条件を定義できます。現在のスコープは、その条件が満たされた場合にのみ実行を継続します。これらは、構文guard condition else {...}
で定義されます。
したがって、オプションのバインディングでそれらを使用するには、これを行うことができます:
guard let number = anOptionalInt else {
return
}
(ガード本体内で、mustのいずれかを使用して、 コントロール転送ステートメント のスコープを終了することに注意してください現在実行中のコード)。
anOptionalInt
に値が含まれている場合、値はラップ解除され、新しいnumber
定数に割り当てられます。ガードのコードafterが実行を継続します。値が含まれていない場合-ガードは括弧内のコードを実行します。これにより制御が移り、直後のコードは実行されません。
ガードステートメントの本当のすてきなところは、ラップされた値がステートメントに続くコードで使用できるようになったことです(オプションが値を持つ場合、将来のコードはonly実行できることがわかっているため)。これは、複数のifステートメントをネストすることで作成された 「Doom of Doom」 を排除するのに最適です。
例えば:
guard let number = anOptionalInt else {
return
}
print("anOptionalInt contains a value, and it’s: \(number)!")
ガードは、複数のオプションを同時にアンラップし、where
句を使用するなど、ifステートメントがサポートしたのと同じきちんとしたトリックもサポートします。
Ifステートメントまたはguardステートメントを使用するかどうかは、将来のコードに値を含めるためにオプションが必要かどうかによって決まります。
Nil Coalescing Operator は、 三項条件演算子 の気の利いた短縮版で、主にオプションを非オプションに変換するように設計されています。構文はa ?? b
です。ここで、a
はオプションのタイプであり、b
はa
と同じタイプです(通常はオプションではありません)。
基本的に、「a
に値が含まれている場合は、展開します。そうでない場合は、代わりにb
を返します」。たとえば、次のように使用できます。
let number = anOptionalInt ?? 0
これは、number
型のInt
定数を定義します。これには、値が含まれている場合はanOptionalInt
の値が含まれ、そうでない場合は0
が含まれます。
以下の略記です:
let number = anOptionalInt != nil ? anOptionalInt! : 0
Optional Chaining を使用して、オプションを呼び出したり、オプションのプロパティにアクセスしたりできます。これは、使用時に変数名の末尾に?
を付けることで簡単に行えます。
たとえば、オプションのfoo
インスタンス型の変数Foo
があるとします。
var foo : Foo?
foo
で何も返さないメソッドを呼び出したい場合は、次のようにします。
foo?.doSomethingInteresting()
foo
に値が含まれている場合、このメソッドはその値に対して呼び出されます。そうでない場合、悪いことは何も起こりません。コードは単に実行を続けます。
(これは、Objective-Cでnil
にメッセージを送信する場合と同様の動作です)
したがって、これはプロパティの設定やメソッドの呼び出しにも使用できます。例えば:
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
にオプションのプロパティbaz
があり、プロパティqux
がある場合、次のように記述できます。
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
と同様に機能しますが、クロージャー本体内からanotherオプションを返すことができます。これは、非オプション入力を必要とするプロセスにオプションを入力できるが、オプション自体を出力できることを意味します。
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?
は、エラーの処理が重要でないまれな場合に使用します。
ごくわずかな例外 を使うと、この規則は重要です。
!
の使用を避けます?
)を宣言します。(!
)言い換えれば、むしろ使用する: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)")
この答えは簡潔にすることを目的としていました、 完全な理解のために受け入れられた答えを読んでください
この質問は ALL THE TIME SOで出てきます。これは、新しいSwift開発者が最初に苦労することの1つです。
Swiftは値を含むことができるかどうかにかかわらず、値を扱うために "Optionals"の概念を使用します。 Cのような他の言語では、変数に値0が入っていないことを示すために、変数に値0を格納することがあります。しかし、0が有効な値の場合はどうなりますか?それから-1を使うかもしれません。 -1が有効な値の場合はどうなりますか?等々。
Swift optionalsを使用すると、任意の型の変数に有効な値を含めるか、または値を含まないように設定できます。
変数を意味すると宣言するときは、型の後に疑問符を付けます(型x、または値なし)。
オプションは、実際には指定された型の変数を含むか、または何も含まないコンテナです。
値を内部で取得するには、オプションを「ラップ解除」する必要があります。
「!」 operatorは "unwrap"演算子です。それは "私を信頼します。私は自分のしていることを知っています。このコードが実行されるとき、変数がnilを含まないことを保証します。"あなたが間違っているならば、あなたは墜落します。
あなたが本当に do あなたがしていることを知らない限り、 "!"は避けてください。オペレータをアンラップします。それはおそらくSwiftプログラマーを始めた頃の最大のクラッシュの原因でしょう。
より安全なオプションを扱う他の方法がたくさんあります。ここにいくつかあります(網羅的なリストではありません)
このオプションに値が含まれている場合は、「オプションのバインド」または「if let」を使用して、その値を新しいオプションではない変数に保存します。 ".
これがfoo
optionalとのオプションの束縛の例です:
if let newFoo = foo //If let is called optional binding. {
print("foo is not nil")
} else {
print("foo is nil")
}
オプションのバインディングを使用するときに定義した変数は、ifステートメントの本体内にのみ存在します( "範囲内"にのみ存在します)。
代わりに、guardステートメントを使うこともできます。これは、変数がnilの場合に関数を終了させることができます。
func aFunc(foo: Int?) {
guard let newFoo = input else { return }
//For the rest of the function newFoo is a non-optional var
}
Swift 2では、Guardのステートメントが追加されました。Guardを使用すると、コードを介して「黄金のパス」を維持し、「if let」オプションバインディングを使用することによって生じることがある.
"nil合体演算子"と呼ばれる構成体もあります。形式は "optional_var ?? replacement_val"です。 optionalに含まれるデータと同じ型の非オプション変数を返します。オプションが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を学んでいるときは、「!」のふりをしてください。文字は言語の一部ではありません。あなたを困らせる可能性があります。
上記の答えはオプショナルと安全に遊ぶ方法を明確に説明しているので。私はOptionalsが本当にSwiftにあるのかを説明しようと思います。
オプションの変数を宣言するもう1つの方法は、
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)
Swiftによると、 'nil'は価値の欠如です。 nil
で初期化されたインスタンスを作成するために我々はExpressibleByNilLiteral
と呼ばれるプロトコルに準拠しなければなりません、そしてもしあなたがそれを推測したならばOptionals
がExpressibleByNilLiteral
に準拠し、他の型に準拠することはお勧めできません。
ExpressibleByNilLiteral
はinit(nilLiteral:)
と呼ばれる単一のメソッドを持ち、インスタンスをnilで初期化します。通常、このメソッドは呼び出さないでください。Swiftのドキュメントによれば、nil
リテラルでOptional型を初期化するときはいつでも、コンパイラが直接このイニシャライザを呼び出すのはお勧めできません。
私自身でさえ私の頭をOptionalsの周りにラップする必要があります(駄目ではありません):D Happy Swfting All 。
まず、Optionalの値が何かを知っておく必要があります。 /に進むことができます Swift Programming Launage
詳しくは.
次に、オプションの値には2つのステータスがあります。 1つは全額、もう1つはゼロの値です。そのため、オプションの値を実装する前に、どの状態になっているのかを確認する必要があります。
if let ...
やguard let ... else
などを使うことができます。
もう1つの方法は、実装前の状態を確認したくない場合は、代わりにvar buildingName = buildingName ?? "buildingName"
を使用することもできます。
私は次のようにprepare for 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
}
私はこの答えが私と同じ問題を抱えている誰かを助けてくれることを願っています。
基本的に、Swiftがnil以外の値のみを許可する場所でnil値を使用しようとしました。コンパイラにはnil値が存在しないことを信頼してもらい、アプリケーションをコンパイルできるようにします。
この種の致命的なエラーにつながるいくつかのシナリオがあります。
強制的に展開します。
let user = someVariable!
someVariable
がnilであれば、クラッシュするでしょう。強制的に展開することで、nil checkの責任をコンパイラからあなたに移します。基本的に、強制的に展開することで、コンパイラにはnil値がないことを保証します。そして、どういうわけかnil値がsomeVariable
で終わっていたらどうなるのでしょうか。
溶液?オプションの束縛(別名if-let)を使い、そこで変数処理を行います。
if user = someVariable {
// do your stuff
}
強制(ダウン)キャスト:
let myRectangle = someShape as! Rectangle
ここに強制キャストすることで、Rectangle
インスタンスが常にあるので、あなたはもう心配しないようにコンパイラに伝えます。そしてそれが成り立つ限り、あなたは心配する必要はありません。プロジェクトのあなたやあなたの同僚が長方形以外の値を循環させ始めると問題が始まります。
溶液?オプションの束縛(別名if-let)を使い、そこで変数処理を行います。
if let myRectangle = someShape as? Rectangle {
// yay, I have a rectangle
}
暗黙的にラップされていないオプション。次のようなクラス定義があるとしましょう。
class User {
var name: String!
init() {
name = "(unnamed)"
}
func nicerName() {
return "Mr/Ms " + name
}
}
name
に設定してnil
プロパティに戸惑う人がいない場合は期待通りに動作しますが、User
がname
キーのないJSONから初期化されている場合は、このプロパティを使用しようとすると致命的エラーが発生します。 。
溶液?それらを使用しないでください:)あなたがそれが使用される必要がある時までに、プロパティが常にnil以外の値を持つことを102%確実でない限り。ほとんどの場合、オプションまたは非オプションへの変換はうまくいきます。それをオプションではないものにすることはまた、あなたが逃したコードパスにそのプロパティに値を与えることを伝えることによってあなたを助けるコンパイラをもたらすでしょう。
未接続、またはまだ接続されていないコンセントこれはシナリオ#3の特別な場合です。基本的にはあなたが使いたいXIBロードクラスがあります。
class SignInViewController: UIViewController {
@IBOutlet var emailTextField: UITextField!
}
あなたがXIBエディタからアウトレット(コンセント)を接続するのを見逃したなら、アプリはアウトレットを使いたいと思うとすぐにクラッシュするでしょう。溶液?すべてのコンセントが接続されていることを確認してください。または、?
演算子を使用してください:emailTextField?.text = "[email protected]"
。または、アウトレットをオプションとして宣言します。ただし、この場合、コンパイラはコード全体のアンラップを強制します。
Objective-Cに由来する値であり、これにはnull許容性の注釈はありません。次のObjective-Cクラスがあるとしましょう。
@interface MyUser: NSObject
@property NSString *name;
@end
NULL可能性アノテーションが(明示的にまたはNS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
を介して)指定されていない場合、name
プロパティはSwiftにString!
(IUO - 暗黙的にラップ解除されたオプション)としてインポートされます。いくつかのSwiftコードがその値を使いたいと思うようになるとすぐに、name
がnilであるとクラッシュします。
溶液? Objective-Cコードにnull許容性の注釈を追加します。ただし、Objective-Cコンパイラはnull許容度になると少し寛容になります。明示的にnonnull
とマークしていても、最終的にはnil値になる可能性があります。
エラーEXC_BAD_INSTRUCTION
およびfatal error: unexpectedly found nil while unwrapping an Optional value
は、@IBOutlet
を宣言したがストーリーボードには関連していない場合に最も多く表示されます。
また、他の回答で言及されているOptionals _がどのように機能するのかについても学ぶ必要がありますが、これが私によく現れる唯一の時です。