Swiftのハテナとビックリ

Swiftを触り始めてすぐ出てくる特徴として”?”と”!”を用いた記述があります。
これらは一体どんな意味を持っていて、どのように使うのでしょうか。

実はひとつだけではなく複数の意味合いがある(これが理解を難しくする主な原因です)ため、
ひとつずつ順を追って見ていきましょう。

 

optional (?その1)

Swiftでは、値が存在しない可能性がある場合通常のデータ型ではなく
オプショナル型(optional)と呼ばれる型を用います。

let a : Int = nil // Error
let b : Int? = nil // OK

通常のデータ型指定の語尾に”?”をつけることでオプショナル型となります。
オプショナル型は通常のデータ型とは別の扱いとなるため、
値が詰まっていたとしてもそのままでは代入することができません。

let a : Int? = 10
let b : Int = a // Error

 

forced unwrapping (!その1)

オプショナル型を代入する際に出るエラーを解決するには複数の方法があるのですが、
そのうちの1つが強制アンラップ(forced unwrapping)と呼ばれる記述です。

let a : Int? = 10
let b : Int = a! // OK、10が代入される

この方法はその名の示す通り、強制的にアンラップ(包装を解く)することで代入を可能にしています。
つまり、オプショナル型はnilが入っている可能性があるが、その特徴を無視して値が入っていると信頼するのです。
そのため、たとえオプショナル型にnilが入っていたとしてもビルドが通ってしまい、
信頼が裏切られた結果としてその箇所の処理を実行する際にアプリがクラッシュしてしまいます。

let a : Int? = nil
let b : Int = a! // ビルドは通るが、処理時にアプリがクラッシュする

安易な強制アンラップは想定していないクラッシュを呼び起こす原因となってしまうため、注意が必要です。

 

optional binding

では、強制アンラップを使わずにどのように値を取り出せばいいのでしょうか。
その問題を解決するのが、optional bindingと呼ばれる記述です。
この記述自体には?や!は出てこないのですが、頻繁に使われる記述のため一緒に説明しておきます。

let a : Int? = 10

if let b = a {
print("aの値は\(b)です")
} else {
print("aはnilです")
}
// 実行結果:"aの値は10です"

このif let b = a {}という記述がoptional bindingにあたります(そのままif let文とも呼ばれます)。
上記を見て頂ければなんとなく分かるかと思いますが、aがnilかどうかをチェックし、
nilではなかった場合はbに値を代入した上でthenの中へ(bは非オプショナルInt型となります)、
nilだった場合はelseの中の処理をそれぞれ実行します。
そのため、1行目がlet a : Int? = nilだった場合は”aはnilです”を出力します。

この記述は実際のコーディングでも頻繁にお世話になるため、覚えておきましょう。

 

nil coalescing operator (?その2)

nilかどうかで処理自体が分かれる場合はoptional bindingで良いのですが、
nilではなかったらこの値、nilだったらこの値を使って処理を行う、といった場合には
nil合体演算子(nil coalescing operator)を用いることで対応します。

let a : Int? = nil
let b = a ?? 0 // bには0が代入される

let c : Int? = 10
let d = c ?? 0 // dには10が代入される

a ?? 0という記述がnil合体演算子と呼ばれるもので、
nilではなかった場合は左辺を、nilだった場合は右辺を用いて処理が実行されます。
省略せずに書いた場合はlet b = (a != nil) ? a! : 0となります。

 

implicitly unwrapped optional (!その2)

しかし、いちいちアンラップをせずとも確実に値が入っていると判断できる場合があります。
その場合、暗黙的アンラップオプショナル型(implicitly unwrapped optional)に代入することで、
余計なアンラップ記述をせずとも値を使うことができます。

let a : Int? = 10
let b : Int! = a // 10が代入される

ただし、こちらも強制アンラップと同じくnilかどうかを判断せずアンラップするため、
もしnilを暗黙的アンラップオプショナル型に代入してしまった場合、こちらもアプリがクラッシュする原因となります。

let a : Int? = nil
let b : Int! = a // 代入自体はできるが、bを使おうとするとクラッシュする

個人的に一番混乱するポイントが、
この暗黙的アンラップオプショナル型は最初に紹介したオプショナル型と同じデータ型の概念となります。
そのため、強制アンラップとは、

  • 強制アンラップ:通常のデータ型へ代入する際に暗黙的にアンラップする
  • 暗黙的アンラップオプショナル型:代入されたオプショナル型を暗黙的にアンラップして使うデータ型

という違いがあります。自分で書いていても混乱します・・・
とりあえず最初は違うものなんだということだけ覚えておけば大丈夫かと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>