【Swift】オプショナル型変数の使い方。変数にデータが設定されないことで起きる不具合を防止する。(Swift 2.1、XCode 7.2)
オプショナルの概要
JavaやC言語などでシステム開発をした場合、変数にNull(データ無し)が設定されたために予期しない動作をした経験がある。
もちろん、Nullチェックが漏れていたための設計者のミスではあるが、Swiftにはnil(データ無し)を起因とした想定外の動作を防止するためにオプショナルという仕様が用意されている。
まず、Swiftの変数はそのままではnilを設定することはできない。設定しようとするとエラーが発生する。
1 2 3 4 5 6 |
var test1:String = "こんにちは" test1 = nil // 実行結果 // error: nil cannot be assigned to type 'String' |
変数にnilが設定できないということは、変数にnilが設定されて渡ってくることが無い。つまり、nilチェックをする必要が無いということだ。これにより、「この変数は本当にNullチェックが必要無いのか?」といった不安から解放される。
しかし、変数によってはnil(データ無し)を許容しなければならないことがある。例えば、以下のコードのようにInt型にキャストするときに整数として評価できなかったとき、エラー発生ではなく、データ無しとして後続処理を行うといった場合だ。
1 2 3 4 5 |
var test2:Int = Int(test1) // 実行結果 // error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'? |
オプショナル型の使い方
以下のコードのように、データ型の後ろに「?」をつければオプショナル型としてnilを設定できるようになる。元のデータ型をオプショナル型で包む(ラップする)イメージだ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//オプショナル型の宣言 var test1:String? = "こんにちは" test1 = nil print(test1) // 実行結果 // nil //別の書き方 var test2:Optional<String> = "こんばんは" test2 = nil print(test2) // 実行結果 // nil |
オプショナル型は、そのまま演算式の中で使おうとするとエラーが発生する。
1 2 3 4 5 6 7 |
var test1:String? = "こんにちは" var test2:String? = "今日はいい天気ですね" print(test1 + test2) // 実行結果 // error: value of optional type 'String?' not unwrapped; did you mean to use '!' or '?'? |
オプショナル型のデータを使うには変数の後ろに「!」をつける。これをアンラップという。包んでいたラップを剥がすイメージ。
1 2 3 4 5 6 7 |
var test1:String? = "こんにちは" var test2:String? = "今日はいい天気ですね" print(test1! + test2!) // 実行結果 // こんにちは今日はいい天気ですね |
ただし、値にnilが設定されている変数をアンラップするとエラーが発生するので注意が必要だ。
1 2 3 4 5 6 7 |
var test1:String? = nil var test2:String? = "今日はいい天気ですね" print(test1! + test2!) // 実行結果 // fatal error: unexpectedly found nil while unwrapping an Optional value |
なので、オプショナル型変数は以下のコードのようにnilチェックをしたあとに利用する。
1 2 3 4 5 6 7 8 |
var test1:String? = nil var test2:String? = "今日はいい天気ですね" if test1 != nil && test2 != nil { print(test1! + test2!) } //出力無し |
アンラップ不要のオプショナル型変数
オプショナル型変数はアンラップしないと演算子の中で使えないが、宣言するときに「?」では無く「!」を付けるとアンラップしなくてもシステムが自動で値を開示してくれる。
1 2 3 4 5 6 7 |
var test1:String! = "こんにちは" var test2:String! = "今日はいい天気ですね" print(test1 + test2) // 実行結果 // こんにちは今日はいい天気ですね |
アンラップを考える必要が無くなって便利なように思えるが、エラーが発生する条件は同じなので、絶対にnilになることは無いという確信があるとき以外は使わない方が良さそうだ。
例えば、インスタンス生成時にすべての変数をnilで宣言し、初期化処理の中で値を全て設定する。そのあとは値がnilになることはが無い仕様で使えそうだ。
1 2 3 4 5 6 |
class Person { var id:Int! //ID var name:String! //名前 var age:Int! //年齢 } |
オプショナルバインディングの使い方
便利な機能にオプショナルバインディングがある。以下のコードのように、if文やwhile文の条件式で「var 変数名 = オプショナル型変数」と記述すると、オプショナル型変数がnilでは無い場合は変数に値がセットされてtrueが戻る。しかも、条件式で値がセットされた変数はアンラップせずに使うことができるのだ。
1 2 3 4 5 6 7 8 9 10 |
var test1:Int? = 100 if var price = test1 { print("5個で\(price * 5)です") } else { print("変数の値がnilです") } // 実行結果 // 5個で500です |
ただし、if文の条件式で宣言した変数はif文のスコープを抜けると使えなくなる。
1 2 3 4 5 6 7 8 9 10 11 |
var test1:Int? = 100 if var price = test1 { print("5個で\(price * 5)です") } print(price) // 実行結果 // error: use of unresolved identifier 'price' |
オプショナルチェインニングの使い方
オプショナル型のプロパティやメソッドなどを呼び出したとき、オプショナル型変数の値がnilだったら通常はエラーが戻る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 文字数をカウント var test1:String? = "朝ごはん" print(test1!.characters.count) // 実行結果 // 4 // 文字数をカウント var test2:String? = nil print(test2!.characters.count) // 実行結果 // fatal error: unexpectedly found nil while unwrapping an Optional value |
オプシュナル型変数の後ろに「?」をつければ、エラーを発生せずにnilを返すようになる。これをオプショナルチェインニングという。
1 2 3 4 5 6 7 |
// 文字数をカウント var test3:String? = nil print(test3?.characters.count) // 実行結果 // nil |
オプショナル型は慣れるまでに時間がかかるが、安全安心なアプリ開発には欠かせない存在になりそうだ。