# Optional型
Swiftでは、値がない状態を nil
で表します。しかし、Swiftの変数は基本的にnilを許容しません。
var a: String = "Hello"
a = nil
/* 実行結果 */
// 'nil' cannot be assigned to type 'String'
// a = nil
変数がnilを許容するにはOptional型を使用する必要があります。
# Optional型の宣言
Optional型の宣言をするときは、変数の後ろに ?
を記述します。
Optional型にすると、変数の宣言と同時に自動的にnilが代入されます。
var a: String? // nil
この ?
はシンタックスシュガーで、 Optional<Wrapped>
を使用すると同じように書くことができます。
次の例では、Optional<String>
型で、文字列とnilを許容することができます。
var a: Optional<String>
a = nil // nil
a = "hello" // hello
TIP
Optional<Wrapped>
は列挙型で、.noneと.someの2つのケースを定義しています。
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
.none
は値が存在しないケースで、nilに等しいケースです。
.some
は値が存在するケースになります。
var a = Optional<String>.none // nil
var b = Optional<String>.some("Hello") // Optional(Hello)
上記は同じ意味になりますが、簡潔に書ける ?
を使用することが一般的です。
# Optinal型のアンラップ
Optional型は値を保持していない可能性があるため、非オプショナル型の変数と同じように扱う事ができません。
例えば、Int型とInt?型を計算しようとするとコンパイラはエラーを出力します。
var int1: Int = 1
var int2: Int? = 2
var result = int1 + int2
/* 実行結果 */
// error: value of optional type 'Int?' must be unwrapped to a value of type 'Int'
// var result = int1 + int2
// ^
Optional型の値に対して操作を行う場合は、一度値を取り出す必要があります。 その値を取り出すことをアンラップといいます。
アンラップをするには主に次の方法があります。
# Optional Binding
Optional Bindingは、条件分岐や繰り返しの条件で使用することができます。if-let文のフォーマットで記述し、Optional型の値が存在するときは、true
を返し、nilのときは false
を返します。
if let 定数名 = Optional型の値 {}
次の例では、Optional型の変数 a
からアンラップしています。値が nil なのでfalse判定になります。
var a: Int?
// Optional型からアンラップする
if let b = a {
print(b)
} else {
print("値はありません")
}
/* 実行結果 */
// "値はありません"
# guard
また、Swift2以降で提供された、guard文
でもOptional Bindingができます。 guard文は、条件が成立しないときに実行される構文です。
次の例では、変数 num
がnilのときはfalse判定になり、returnをします。値がある場合は、そのまま処理が続行されます。
func testGuard(num: Int?) {
// nilだとreturnする
guard let unwrappedNum = num else { return }
print(unwrappedNum)
}
testGuard(num: 5) // 5
guard文の詳しい説明は、制御構文の章でご確認ください。
# ??演算子
??演算子は、値が存在しない場合にデフォルトの値を代入することができます。
??
の右辺に代入したいデフォルト値を指定してください。
var a: Int?
var b = a ?? 3
print(b) // 3
a
には値が存在しないので、b
にデフォルト値である 3
が代入されます。
値がある場合は、デフォルト値はスキップされます。
var a: Int? = 1
var b = a ?? 3
print(b) // 1
# !演算子で強制アンラップ
!演算子は、強制的にOptional型の値を取り出すことができます。
var a: Int? = 1
var b = a! + 1 // 2
強制的に値を取り出すため、もし値がないときは実行時にエラーが発生します。
var a: Int?
var b = a! + 1
/* 実行結果 */
// Fatal error: Unexpectedly found nil while unwrapping an Optional value
// Current stack trace:
Swiftは静的型付け言語であり、コンパイル時にエラーを検出することでプログラムの安全性を確保しています。
強制アンラップは、プログラムの実行時にエラーが発生するので、意図しないバグを招く可能性があります。
変数の後ろに、!
をつけるだけなのでシンプルに書くことができますが、値の存在が明らかな場合や値がないときはプログラムを終了させたい場合のみ使用するのがいいでしょう。
# 暗黙的にアンラップ
Optional型を宣言する際には、?
を使用し、アンラップする際には !
を使用して値を取り出しました。
宣言時に、 !
をつけることでアンラップを明示的にしなくても自動的に値を取り出すことができます。
var a: Int! = 10
var b = a + 10 // 20
// 以下と同じ
// var a: Int? = 10
// var b = a! + 10
暗黙的にアンラップされたOptional型はアクセス毎に強制アンラップされるので、非Optional型のように扱えます。
しかし、nilであった場合は同様に実行時エラーが発生します。
var a: Int!
var b = a + 10
/* 実行結果 */
// Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
こちらも、意図せぬエラーを招く可能性があるため、強制アンラップ同様に多用されるのは避けるべきでしょう。
# Optional Chaining
Optinal Chainingは、アンラップをせずにメソッドやプロパティにアクセスすることができます。
アクセスする際は、?
を変数の前につけます。
通常、Optional型のプロパティにアクセスする際はアンラップしてから書くことができます。
var a: Int? = 10
var c: Int
if let b = a {
c = b.distance(to: 1)
print(c) // -9
}
Optional Chainingを使用すると、アンラップせずにアクセスすることができます。
?
を変数の前に記述して、 distance()
にアクセスします。
var a: Int? = 10
print(a?.distance(to: 1)) // Optional(-9)
Optional Chainingの結果はOptional型になります。値を取り出したいときは、Optional Bindingを併用して書くことができます。
var a: Int? = 10
if let c = a?.distance(to: 1) {
print(c) // -9
}
もし、値がない場合は nilを返します。
var a: Int?
if let c = a?.distance(to: 1) {
print(c)
} else {
print("値がありません")
}
// 値がありません
# mapメソッド
mapメソッドを使うと、アンラップをせずに値の取得をすることができます。
mapメソッドは、値が存在すれば実行し、値が存在しなければ何も実行されません。
let a: Int? = 10
let b = a.map { val in val * 10 } // Optional(100)
# Null Safety
モダンなプログラミング言語では、Null Safetyという仕組みが備わっています。Null Safetyとは、null参照時に実行時エラーを防ごうという考え方です。(Swiftだとnil)
例えば、JavaScriptでは、nullに対してアクセスすると実行時にエラーが発生します。
const s = null;
s.length
// Uncaught TypeError: Cannot read property 'length' of null
JavaScriptは、Null Safetyな言語ではありません。そのため、プログラムを書いた段階ではエラーを見つけることができません。
一方、SwiftはNull Safetyな言語です。
Optional型が提供されているため、nilになる可能性がある値に対して直接処理を実行すると、コンパイル時にエラーになります。
var a: String?
var b = a.uppercased()
/* 実行結果 */
// error: value of optional type 'String?' must be unwrapped to refer to member 'uppercased' of wrapped base type 'String'
// var b = a.uppercased()
Swiftはこのような型の安全性を提供し、開発者が明示的にアンラップなどをしない限りはプログラムを実行できない仕組みになっています。
# 値の取り出しの使い分け
Swiftは、様々なOptional型の値の取り出し方を提供しています。
型の安全型を考慮して、次のような使い分けがいいでしょう。