2014年6月5日木曜日

Swift言語の入門 (6) オプション型編

Swiftの言語仕様を読んでいくと、はじめにみつかる特徴的な仕様に、オプション型があります。オプション型とは、名前の通り、必ずしも存在する訳ではない、と言った意味合いがあり、その型そのものの値を含むか、もしくはその値が存在しないことを意味する型です。

他の言語ではNullableなどと呼ばれていることもありますが、Swiftでは全ての型にオプション型が用意されており、IntやDoubleに対しても利用することができます。

JavaではNullableというタイプアノテーションがありますが、これはプリミティブ型(値型)には対応しておらず、C#のNullableは逆に値型にのみ対応しています。Swiftではどちらの種類の型のオプション型を作ることができるため、IntやDoubleなどの型や、独自のクラスのためのオプション型を作ることができます。

1.宣言


var optionalName: String? = "John Appleseed"

オプション型の宣言は、ただ単に型の最後に「?」を追加するのみです。これをつけることにより、optionalNameにnilを代入することができるようになります。
Swiftでは、オプション型でない方にはnilを代入することができず、コンパイルエラーとなるので、逆を言えばオプション型出なければ必ず値は存在しており、nilかどうかチェックする必要は全くありません。

2.オプション型のアンラップ


オプション型からオプション型でない元の型をアンラップ(unwrap)ためには、いくつかの方法があります。

2.1 if let 構文によるアンラップ

if let name = optionalName {
    greeting = "Hello, \(name)"

}

このように、if文の条件の中にlet文を書くことで、「optionalNameが値を持っていた場合には、その値をnameという変数に代入して { ... } 内を実行する」という意味のコードがかけます。optionalNameが値を持たなかったときの処理を書きたいときには、elseの中に書くこともできます。

2.2 「!」演算子によるアンラップ

また、オプション型として宣言したけれども、nilではない値を代入した直後等のような、プログラマの目から見て明らかな場合に関しては、

greeting = "Hello, \(optionalName!)"

のように、変数の末尾に「!」をつけることで強制的に元の型に変換することができます。
ただし、この方法を利用して取り出した値がnilであった場合にはランタイムエラーが起きてしまうので、使う際には注意が必要です。しかしその分いちいちnilかどうかのチェックを行わないので、nilでないことが明らかな場合には、オーバーヘッドを少なくすることが可能です。

2.3 「?」演算子によるアンラップ

メソッドチェーン等を行う際に重宝する書き方ですが、

optionalName?.isEmpty

と書くことで、optionalNameに値があった場合は全体としてisEmptyの戻り値に、nilであった場合は全体としてnilになるという書き方もできます。

この式が評価されるときには、内部的にoptionalNameがnilかどうかを判定して、nilであればその場でnilと評価し、そうでなければそのまま計算を続けるような処理がなされます。

もともとObjective-Cではnilに対するメソッド呼び出しは無視されるという仕組みが備わっていたので、その機構とほぼ同一のものとしてみてよいでしょう。

3.暗黙的にアンラップされるオプション型


元の名はImplicitly Unwapped Optionalsですが、すなわち勝手にアンラップを行ってしまうようなオプション型、ということです。挙動としてはObjective-Cの通常の変数のように、nilも入るし値もはいる型で、nilをアンラップしてしまったときはクラッシュするという型です。挙動的にはJavaの参照型のようなイメージですが、これは何のために必要なのでしょうか。

宣言方法は、「?」のかわりに「!」を型の最後に書くだけです。

  • let assumedString: String! = "An implicitly unwrapped optional string."

使い方としての違いは、値を利用する際に今まで必要だったアンラップの処理がいらないことです。

println(assumedString)

もちろん明示的にif let文を使ってアンラップすることも可能です。

  • if let definiteString = assumedString {
  • println(definiteString)
  • }

このタイプのオプション型の場合には、「!」によるアンラップと同様に、逐一nilのチェックを行わないため、オーバーヘッドが「?」のオプション型よりも小さくなります。

これだけでは使いどころがさっぱり不明ですが、これは主にARCのリファレンスカウンタにおける循環参照の問題を処理するために導入された仕様のようで、あえて「!」にすることによりアクセスのためのオーバーヘッドを減らしつつ、初期化の際に一瞬nilの瞬間を作り出すことによって、循環参照の問題を起こさなくすることができるそうです。詳細はまた別の回にて行いたいと思います。

総括

  • オプション型は全ての型に対して作れる
  • 例外的値等を処理する際に重宝する
  • 「?」はnilだったら無視される
  • 「!」はオーバーヘッドが無いかわりにnilだとクラッシュ
いらない情報
オプション型のオプション型も作れるようで、String!?ともかけたりするようです。

Swift関連記事

0 件のコメント:

コメントを投稿