オプショナル型とは
オプショナル型とはデータ型の一つで、変数の宣言時に使用します。
特徴としては変数にnil
の代入を許可することで、逆に非オプショナル型はnil
の代入を許可しないことになります。
nil
とはデータが無い(変数が空の状態)を表します。
iOSではnil
に対して操作するとアプリケーションが落ちてしまうことがあり、そのような問題を解決するためにswift
ではnil
を基本的には許容しないとのことです。しかし、オプショナル型を使うことでnil
を扱うことができるようになります。
オプショナル型の使い方
// オプショナル型
var age: Int?
var name: String!
// 非オプショナル型
var age: Int
var name: String
オプショナル型は最後に?
か!
をつけます。非オプショナル型は最後に何もつけません。
また、一般的にオプショナル型 ( Optional Value ) というと?
、!
は暗黙的アンラップ型 ( Implicitly Unwrapped Optional )と呼ばれるオプショナル型になります。
非オプショナル型の変数は普通に扱う変数で、nil
を扱うことのできるオプショナル型の変数は通常とは扱い方が少し異なります。オプショナル型の変数を通常の変数と同様に扱おうとするとおもわぬエラーが起きるため、オプショナル型の値を通常の値に変換するアンラップという方法が必要になります。
暗黙的アンラップ型
一般的なオプショナル型と違い、使用するとき必ず強制的にアンラップされます。利点として、初期値はないが後から必ず値を入れる保証があるものに使うと効果を発揮します。
なぜアンラップが必要なのか?
まずは、オプショナル型と非オプショナル型の出力結果の違いを見ていきます。
// オプショナル型
var name: String?
name = "Taro"
print(name) // 出力結果 Optional("Taro")
// 非オプショナル型
var name: String
name = "Taro"
print(name) // 出力結果 Taro
上記の通り、オプショナル型は Optional("Taro") 、非オプショナル型は Taro と出力されていることから大きな違いがあるとわかります。
また、__「Optional(値)」と「値」__は異なるもので、同様には扱うことができません。
var a: Int? = 10 // オプショナル型
var b: Int = 10 // 非オプショナル型
a + b
/*
-> a + b はエラーとなる
Value of optional type 'Int?' must be unwrapped to a value of type 'Int'
オプションタイプ「Int?」の値 タイプ「Int」の値にアンラップする必要があります。
*/
結果はエラーとなり実行することができません。
なぜなら、変数a
は Optional(10) 、変数b
は 10 という値であるため、Optional(10) + 10
は計算することができないということになります。
ここで、オプショナル型の値を通常の値 ( Optiona(10)
を10
) に変換するためにアンラップという処理が必要ということになります。
アンラップとは
Optional
は値を包み込むラップのようなイメージで、オプショナル型は値をOptional
というラップで包み込んでいます。そうすると、たとえ中身がない ( nil
) 状態でも包み紙だけは存在するため、扱うことができます。
しかし、この便利な包み紙があるために値はラッピングされ、直接扱うことができません。扱うためにはラップを取り除き、中身の値を取り出さなくてはいけません。この包み紙を取り除き、値を取り出すことをアンラップと言います。
アンラップの種類
1. 強制的アンラップ
そのままの意味になりますが、オプショナル型を強制的にアンラップする方法
です。オプショナル型の変数の中にどんな値が入っていても関係なく、その値を取り出します。
var name: String?
name = "Taro"
print(name!) // 出力結果 Taro
オプショナル型の変数に対して!
をつけ強制的アンラップをすると、出力がTaro
となりました。
簡単に扱える強制的アンラップですが、アンラップする対象のオプショナル型の変数の中身がnil
だった場合、エラーとなりアプリケーションが落ちてしまうという欠点があります。なので、強制的アンラップをする場合は、必ずラッピングの中身が存在する(nil
ではない)ことが保証されていなければいけません。
2. オプショナルバインディング
強制的アンラップはオプショナル型の変数を中身の値に関係なくアンラップしました。そのため変数にnil
が入ってしまうとアプリケーションが落ちてしまうという問題があり、それを解決するアンラップの方法がオプショナルバインディングです。
オプショナルバインディングの特徴は条件式を用いることで、アンラップする対象のオプショナル型の変数がnil
かどうかを判定するためのものです。
if let
-
nil
だった場合に行う処理が異なる時に使用します。 -
nil
の場合は__Aの処理__、nil
ではない場合は__Bの処理__をするケースでよく使います。
let name: String? = "Taro"
if let unwrappedName = name {
//「name」が「nil」ではない場合の処理
print(unwrappedName)
} else {
//「name」が「nil」である場合の処理
print("名前がありません")
}
// -> 出力結果 Taro
guard let
- これ以上処理を進めたくない場合に使用します。
-
nil
が入っていたらエラーとして扱うケースだった場合などによく使います。
let name: String? = "Taro"
guard let unwrappedName = name else { return }
//「name」が「nil」ではない場合の処理を進める
print(unwrappedName)
// -> 出力結果 Taro
3. オプショナルチェイニング
オプショナルチェイニングもオプショナル型の変数の中身がnil
のときに安全にプログラムを実行するための機能です。
オプショナルチェイニングの使い方はオプショナル型の変数のあとに?
をつけます。
オプショナル型の変数?.メソッド()
オプショナル型の変数?.プロパティ
変数!
で強制的にアンラップする場合と、変数?
でオプショナルチェイニングを使用する場合で比較してみます。
変数!
で強制的にアンラップする場合
var str1: String? = "HELLO"
let str2 = str1!.lowercased()
print(str2) // 出力結果 hello
結果としてhello
が表示されます。ただし、変数str1
がnil
の場合にはエラーとなり、プログラムが落ちます。
変数?
で強制的にアンラップする場合
// 変数str1が「nil」でない場合
var str1: String? = "HELLO"
let str2 = str1?.lowercased()
print(str2) // 出力結果 Optional("hello")
// 変数str1が「nil」である場合
var str1: String? = nil
let str2 = str1?.lowercased()
print(str2) // 出力結果 nil
この場合、変数str2
の値はオプショナル型になり、出力結果はOptional("hello")
と表示されます。また、変数str1
がnil
の場合にもエラーとならず、結果はnil
となります。オプショナルチェイニングの結果はオプショナル型となるため、オプショナルバインディングと組み合わせて使用されます。
オプショナルバインディングと組み合わせた使用
var str1: String? = "HELLO"
if let str2 = str1?.lowercased() {
print(str2)
} else {
print("str1はnilです!")
}
// -> 出力結果 hello
??演算子
でnil
であった場合のデフォルト値を設定する
オプショナル型の変数 ?? デフォルト値
??演算子
は、左辺のオプショナル型の変数がnil
でない場合にはその値を戻し、nil
の場合には右辺のデフォルト値を戻します。
var name: String?
let defaultName = "Taro"
let yourName = name ?? defaultName
print(yourName) // 出力結果 Taro
まとめ
オプショナル型についてまとめてみました。私の意見にはなりますが、強制的アンラップ!
はアプリのクラッシュの要因の一つになると思いましたので、基本的には__条件分岐が可能__な if let
を用いてアンラップし、エラー処理の場合は__以降の処理が実行されない__guard let
を用いてアンラップする考え方で行こうと思いました。