この回答はコミュニティWikiです。改善できると思われる場合は、自由に編集してください。
背景:オプションとは何ですか?
Swiftでは、Optional
は(任意の種類の)値を含むか、まったく値を含まないジェネリック型です。
他の多くのプログラミング言語では、特定の「センチネル」値が値の欠如を示すためによく使用されます。たとえば、Objective-Cでは、nil
(nullポインター)はオブジェクトがないことを示します。しかし、これはプリミティブ型を操作するときにさらにトリッキーになります。-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
(これnil
はnil
Objective-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 ?? b
がa
あり、はオプションのタイプでb
あり、と同じタイプですa
(ただし、通常はオプションではありません)。
本質的にa
は、「値が含まれている場合は、ラップを解除します。そうでない場合は、b
代わりに戻ります。」たとえば、次のように使用できます。
let number = anOptionalInt ?? 0
これはnumber
、Int
型の定数を定義します。これには、の値がanOptionalInt
含まれている場合、値が含まれている場合、または0
その他の値が含まれます。
これは以下の略記です:
let number = anOptionalInt != nil ? anOptionalInt! : 0
オプションの連鎖
オプションのチェーンを使用して、メソッドを呼び出したり、オプションのプロパティにアクセスしたりできます。これは、変数名を?
使用するときに、変数名にaを付けることで簡単に行えます。
たとえばfoo
、オプションのFoo
インスタンスタイプの変数があるとします。
var foo : Foo?
foo
何も返さないメソッドを呼び出したい場合は、次のようにするだけです。
foo?.doSomethingInteresting()
場合はfoo
値が含まれ、この方法は、それに呼び出されます。そうでない場合、問題は何も起こりません。コードは単に実行を継続します。
(これはnil
Objective-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
オプションのプロパティを持っている場合、次のように記述できることを意味します。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
を返すことができます。つまり、オプションではない入力が必要なプロセスにオプションを入力できますが、オプション自体を出力できます。
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?
まれなケースでは、エラーの処理が重要ではありません。
資源