技術記事については、Qiitaにも稀に投稿しています。

Swift2でiOSアプリ開発はじめました -オプショナル型-

swift2

iOSアプリ開発の勉強ということで、Swift2の勉強をしているわけですが、せっかくなのでブログに残しておこうと思いました。ググればすぐ出てくるような内容でも自分で書き留めておくのが大事かなと。

というわけで、今回は「オプショナル型」についてまとめてみました。
[toc]

オプショナル型(Optional Type)

Swiftの変数宣言には、"オプショナル型"と呼ばれるデータ型を用いることができます。その反対に、オプショナル型ではないものを"非オプショナル型"などと呼びます。

オプショナル型とは

簡単に言うと、「変数の値に"nil"を許す」データ型のこと、です。"nil"は、他のプログラミング言語で言うところの"NULL"みたいなものです。(空であり、""や0ではない)

オプショナル型と非オプショナル型の変数宣言の違いは、型の後に記号を付けるかどうかです。(「!」と「?」の違いは後述)

オプショナル型

var optStr: String?
var optInt: Int?

var optStr: String!
var optInt: Int!

非オプショナル型

var noOptStr: String
var noOptInt: Int

オプショナル型の初期値はnil

オプショナル型は、nilを代入できるだけでなく、宣言時に初期化しなかった場合にもnilが入ります。対して、非オプショナル型はコンパイルエラーとなります。

オプショナル型

var optStr: String?
print(optStr) // -> nil

非オプショナル型

var noOptStr: String
print(noOptStr) // -> エラー(初期値なし)
noOptStr = nil  // -> エラー(nilは代入できない)

nil以外の値を入れた場合

オプショナル型、非オプショナル型は(nil以外の)値を代入し、それを出力した際にも違いがあります。

オプショナル型

var optStr: String?
optStr = "This is Optional type."
print(optStr) // -> Optional(This is Optional type.)

オプショナル型は上記のように、"Optional()"という記述に囲まれて出力されますが、非オプショナル型の場合、変数の中身だけが下記のように出力されます。

非オプショナル型

var noOptStr: String
noOptStr = "This is NOT Optional type."
print(noOptStr) // -> This is NOT Optional type.

ラップ(wrap)とアンラップ(unwrap)

変数がオプショナル型であることを「ラップ(wrap)されている」と表現します。上記の出力の際のように"Optional()"という記述で囲まれている(包まれている)ことに由来しているのでは。

反対に、ラップされている変数を取り出すことを「アンラップ(unwrap)する」と表現します。

アンラップする方法

大きく以下の3つが挙げられます。他にも比較演算子によるアンラップができるらしい。

  • 強制的アンラップ(Forced Unwrapping)
  • オプショナルバインディング(Optional Binding)
  • オプショナルチェイニング(Optional Chaining)

強制的アンラップ(Forced Unwrapping)

現時点で、最もよく見る(というかXcodeに言われる)アンラップ方法です。計算で使用したり、関数の引数にする場合、値がnil(空)でないことを明言する必要があります。

var price: Int?
price = 500
print("価格:\(price!)円")  // 変数名の後ろに!をつけてアンラップ

price = nil
print("価格:\(price)円")   // nilのままアンラップしなければnilとなる
print("価格:\(price!)円")  // nilはアンラップできない -> エラー

関数の引数として、非オプショナル型の変数(price)を受けとるには関数の呼び出し側でアンラップする必要があります。

var price: Int? = 500
func calculateTax(price: Int) -> Int {
    return Int(Double(price) * 1.08)
}
let includeTaxPrice = calculateTax(price!)  // この呼び出し側でアンラップ

price = nil
let includeTaxPriceE = calculateTax(price!) // nilはアンラップできない -> エラー

オプショナルバインディング(Optional Binding)

if文やwhile文の条件として宣言され、nil以外なら真、nilなら偽となります。また、オプショナル型を代入された変数(または定数)は、非オプショナル型となります。

var myAge: Int? = nil

// myAgeがnilならfalse、それ以外ならtrue
if let p = myAge {
    print("年齢:\(p)歳") // pはアンラップされた非オプショナル型
} else {
    print("年齢:不明")
}

強制的アンラップと異なり、条件式の中でラップされた変数が「nilかどうか」を判断してから処理を行うため、アプリケーションが落ちるといったことなどを防ぎます。

オプショナルチェイニング(Optional Chaining)

これも安全にプログラムを動作させるための仕組みと言えます。オプショナル型の変数のプロパティやメソッドを呼び出す際に使用し、「?」を使ってアンラップします。

但し、オプショナルバインディングとの大きな違いは、返される値がオプショナル型であるということです。(変数がnilだった場合に戻り値としてnilを返す可能性があるため)

引数として与える変数(newPrice)や、プロパティ(item)がオプショナル型だと見づらくなるので、必要なところのみオプショナル型で宣言します。

var newPrice: Int = 500
class Amount {
    var item: String = "Clock"
    func calculate(price2: Int) -> Int {
        return Int(Double(price2) * 1.08)
    }
}

var itp: Amount? = Amount()
// [?]でアンラップしているのがオプショナルチェイニング
print(itp?.calculate(newPrice)) // -> Optional(540)
print(itp!.calculate(newPrice)) // -> 540
print(itp?.item)                // -> Optional(Clock)
print(itp!.item)                // -> Clock

なお、変数がnilだった場合、nilを戻り値として返した後、その後の処理はすべてキャンセルされます。

オプショナル型の変数宣言時の「!」「?」

まず前提として、オプショナル型の変数を宣言する際の「!」「?」と、アンラップする際の「!」「?」は同じ記号であるものの、意味は全く異なります。

アンラップする際は「強制的アンラップ(!)」と「オプショナルチェイニング(?)」という用い方の違いがありましたが、変数宣言においては、オプショナル型というと「?」を指すのが一般的です。

一方で、「!」はnilを許すオプショナル型ではあるものの、暗黙的オプショナル型と呼ばれるオプショナル型です。

暗黙的オプショナル型(Implicitly Unwrapped Optional)

「!」を使用してオプショナル型の変数を宣言した場合にも、nilを許容する通常のオプショナル型とは変わりありません。違いはアンラップ時に現れます。

通常のオプショナル型(宣言時に「?」)

var price: Int?
price = 500
print("価格:\(price!)円")  // 変数名の後ろに!をつけてアンラップ

一方で、暗黙的オプショナル型では出力時のアンラップで「!」を付ける必要がありません。

暗黙的オプショナル型(宣言時に「!」)

var price: Int!
price = 500
print("価格:\(price)円")  // 変数名の後ろに!をつける必要がない

これは便利!と思うかもしれませんが、暗黙的オプショナル型では自動的にアンラップされるので、強制的アンラップと同様、その変数に必ず値が入っている(nilでない)時にのみ使用します。

オプショナル型のまとめ

オプショナル型と非オプショナル型

  • 変数のデータ型で、オプショナル型はnilの代入を許し、非オプショナル型は許さない。(代入しようとするとエラーになる)
  • オプショナル型として変数を宣言する際には、型の後ろに「?」または「!」を付与する。

オプショナル型の出力や参照

  • オプショナル型の変数を出力(print)すると、Optional()という記述で囲まれる(wrapされている)
  • 非オプショナル型と同様に扱うためには、オプショナル型の変数をアンラップ(unwrap)する必要がある

アンラップの方法

  • 強制的アンラップ: 変数の最後に「!」を付けてアンラップする。変数がnilだと実行時エラーが起こる。
  • オプショナルバインディング: ifやwhileの条件式の中で、変数のオプショナル型がnilでない時のみ"true"としてアンラップする。条件式内で代入された変数は非オプショナル型となる。
  • オプショナルチェイニング: 変数の最後に「?」を付け、オプショナル型がnilでない時のみ、プロパティの取得やメソッドの取得を行う。返される値はオプショナル型となる。

暗黙的オプショナル型

  • 出力や参照時に、強制的アンラップされるオプショナル型。変数の宣言時に「?」の代わりに「!」を用いる。

あとは実際に開発をしていくことで使用ポイントを抑えていければいいかな。