[2014/12/06 追記]
記事を書いた当時から半年ほど経過し、Swiftも仕様が色々と変わっているようなので改めて最新のXcodeを使って挙動を調べ直して、記事を更新しました。
細かい仕様は変わっているものの、考え方は以前から大差がないように思いました。
Swiftの発表から間もないですが、既にたくさんの情報が出回ってきてて、ワクワクしますね。
てことで、遅ればせながら ?
とか !
を使った Optional Value について実際に弄って挙動を試してみました。
ちょっと内容が被ってる投稿が他に見受けられましたが、せっかくなので書き留めてみます。
変数宣言
[2014/12/06 追記] 下記は古い説明のコードです。
// 初期値が必要
var required:String = "初期値"
// 初期値は必要なし(nil が入る)
var optional:String?
通常の変数には必ず初期値を入れる必要があります。
?
をつけておけば初期値は必要ありません。( nil
が入る)
[2014/12/06 追記] 下記は古い説明のコードです。
var required:String // コンパイルエラー
通常の変数に初期値を入れないとコンパイルエラーになります。
[2014/12/06 追記]
最新版では、通常の変数も宣言と同時に初期値を入れなくても問題ないようです。
しかし、
var required:String
println(required) // コンパイルエラー
required = "1"
値を入れる前に参照しようとするとコンパイルエラーになります。
var required:String
required = "1"
println(required) // 参照前に値が入ってるので問題無し
参照する前に値を入れてあげれば大丈夫です。
変数への代入
var required:String = "必須"
var optional:String? = "オプション"
required = nil // コンパイルエラー
optional = nil // こっちは大丈夫
通常の変数に nil
を代入する事はできません。常に中身が入っている事を保証しています。
オプショナルな変数は後から nil
を代入する事もできます。
var required:String = "必須"
var optional:String? = "オプション"
required = optional // コンパイルエラー
通常の変数にオプショナルな変数を代入するとコンパイルエラーになります。
nil
が代入されるのを事前に防ぐようになってますね。
var required:String = "必須"
var optional:String?
// コンパイルエラーにならない
// が、代入した時点で実行時エラーになる
required = optional!
変数の後ろに !
をつけておけば強制的にコンパイルエラーの対象から外せるようです。
ただし、実行時に通常の変数に nil
が入ってしまった時点でエラーになるので要注意です。
あまり使ってはいけないケースだと思いますが、後述の if
文の中で使うなど必要な場面もありそうです。
var required:Int = 0
func getInt() -> Int {
return 1
}
func getIntOrNil() -> Int? {
return 1
}
required = getInt() // 問題無し
required = getIntOrNil() // コンパイルエラー
関数の戻り値の場合も同様です。
戻り値の型がオプショナルな場合は通常の変数に代入しようとするとコンパイルエラーになります。
※ちなみに、上記のコードで関数の宣言の前に呼び出しそうとしたらコンパイルエラーになりました。ローカル関数(?)は呼び出す前に宣言する必要があるようです。
変数の評価
[2014/12/06 追記] 下記は古い説明のコードです。
var optional:Int? = 0
if (optional) {
// trueになる
}
オプショナルな変数はそのまま if
文などで評価できます。
この場合は中身が nil
かどうかの判定になるので、上記のように値が 0
であっても true
になります。
[2014/12/06 追記]
最新版では、変数をそのまま評価できなくなりました。
var optional:Int? = 0
if (optional != nil) {
// trueになる
}
比較演算子を使ってちゃんとnilと比較する必要があります。
[2014/12/06 追記] 下記は古い説明のコードです。
var required:Int = 0
// コンパイルエラー
if (required) {
//
}
// こっちは大丈夫
if (required >= 0) {
// trueになる
}
逆に通常の変数は通常の変数もそのままでは評価できず、コンパイルエラーで弾かれます。
ちゃんと比較演算子を使ってあげれば、コンパイルエラーにはなりません。
[2014/12/06 追記]
オプショナルな変数と同様に、通常の変数も比較演算子を使います。
[2014/12/06 追記] 下記は古い説明のコードです。
var required:Int = 0
var optional:Int? = 1
// コンパイルエラー
if (optional) {
required = optional // コンパイルエラー
required = optional! // こっちは大丈夫
}
[2014/12/06 追記] 下記は新しい説明のコードです。
var required:Int = 0
var optional:Int? = 1
// コンパイルエラー
if (optional != nil) {
required = optional // コンパイルエラー
required = optional! // こっちは大丈夫
}
ちなみに、上記のように if
文で中身が nil
でない事が分かっているなら、代入しても大丈夫かなぁと淡い期待をしてみたんですが、コンパイルエラーになるようです。
ちゃんと !
を付けてあげれば大丈夫でした。
変数のメソッド呼び出し
var required:HogeClass = HogeClass()
var optional:HogeClass? = HogeClass()
required.hogeMethod() // 問題無し
optional.hogeMethod() // コンパイルエラー
オプショナルな変数のメソッド呼び出しはそのままではコンパイルエラーになります。
Xcode上ではコード補完もされず、そんなメソッドは知らないと怒られます(笑)。
var optional:HogeClass?
// コンパイルエラーにはならない
optional?.hogeMethod() // nilが返り、実行時エラーにもならない
変数の後ろに ?
を付けておけばメソッドを呼び出せるようになります。
また、変数が nil
だった場合は戻り値も nil
になるので、実行時エラーにもなりません。
※Objective-Cのメッセージ式を使ったメソッド呼び出しでも、 nil
がメソッドを呼び出してもエラーにならないのを踏襲している形かと思います。
var required:Int = 0
var optional:HogeClass?
// コンパイルエラーになる
required = optional?.getInt() // nilが返る
オプショナルな変数のメソッド呼び出しは nil
が返る可能性があるので、通常の変数に代入しようとするとコンパイルエラーになります。
上記の getInt()
メソッドの戻り値がオプショナルではなく確実に値を返す事が保証されていても、 optional
変数が nil
の時点で required
変数も nil
になってしまう可能性があるからです。
感想
個人的には、日頃から
if (obj === null) return;
obj.hoge();
な癖をつけてきたんですが、これらの記述をなるべく減らせるのは嬉しいですね。
[2014/12/06 追記] 下記は古い説明のコードです。
if (str) {
// 空文字""は大丈夫?
}
的な事を考えなくて良いのも素晴らしいなぁと思います。
ではでは。