【初心者向け】Swiftの"?"と"!",はじめからていねいに (1/2)

iOS, Swift

はじめに

初投稿になります.19新卒の河野です.配属後はiOSエンジニアとして,日々精進しています.今回の投稿では,Swiftの1大テーマである”?”と”!”について一度整理しようと思い,このテーマにしました.

既存の解説記事とはやや切り口が異なりますが,個人的にこのまとめ方で理解するのもわかりやすいのでは,と試行錯誤してまとめてみました.

型の後ろの”?”と”!”

Swiftでは変数を宣言する際に型の後ろに”?”・”!”をつけることがあります.これは変数をオプショナル型として宣言する場合につけるものです.Swiftでは変数にnilを代入することができません.nilの代入を許容するには,変数宣言するときにその変数をオプショナル型変数として宣言する必要があります.Swiftのオプショナル型変数にはオプショナル型(Optional Value)と暗黙的アンラップ型(Implicitly Unwrapped Optional)の2種類があります.

オプショナル型変数と非オプショナル型変数は同じInt型であっても異なるデータ型という扱いになります.

したがって,これらを二項演算すると型が異なる変数同士を演算することになるためエラーになります.

このとき,この演算を実行するためにはオプショナル型変数に対して,オプショナル変数に格納されている値を取り出すアンラップ (unwrap)という操作が必要になります.

オプショナル型変数のうち暗黙的アンラップ型は演算が実行されるタイミングで自動的に(暗黙的に)アンラップの操作を行います.

しかし,piyo = nilの場合にアンラップを行なった場合,nilと(nilを許容しない)非オプショナル型を二項演算することになるためエラーが発生します.したがって,nilの可能性があるからという理由で闇雲に変数を暗黙的オプショナル型として宣言することは危険です.

名前の後ろの”?”と”!”

Swiftのコードに出てくる”?”と”!”のうち,変数やメソッドなどの名前の後ろについてくる”?”と”!”は前述のアンラップに関わる操作を行うための記号です.名前の後ろに”?”や”!”をつける処理としてForced Unwrapping (強制的アンラップ)とOptional Chaining (オプショナルチェイニング)をここではまとめます.

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

Forced Unwrapping (強制的アンラップ)とはオプショナル型変数の中にどんな値が入っていてもアンラップをするという方法です.以下の記法でオプショナル型変数を非オプショナル型変数に変換します.

しかし,fugaにnilが入っている場合にもアンラップを強行しようとするため,その場合にはエラーになります.

したがって,Forced Unwrappingによるアンラップを行う場合には,その変数に値が必ず格納されていることが保証されていることを確認して,行う必要があります.(公式ドキュメントにもこの”!”について以下のように記述がありました)

The exclamation mark effectively says, “I know that this optional definitely has a value; please use it.”

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

Optional Chaining (オプショナルチェイニング)とはクラスが持つオプショナル型のプロパティやメソッドを安全に呼び出す方法です.以下の例(公式ドキュメントを引用)をみてください.

このようにPersonクラスを定義した上でPersonクラスのインスタンスjohnのresidence.numberOfRoomsを取得することを考えます.以下の手順でnumberOfRoomsを取得しようとすると,エラーが発生します.

上のケースではオプショナル型のオブジェクトがnilを含む可能性のある状況下でForced Unwrappingをしたためにエラーが発生してしまいました.このような場面において,Optional Chainingを活用すれば,より安全にresidence.numberOfRoomsを取得することができます.

Optional Chainingでは以下の記法でプロパティやメソッドを参照します.

Optional Chainingは,Forced Unwrappingとは異なり,このがnilだった時点で,それ以降のプロパティやメソッドを参照せずにnilを返します.

したがって,強制的なアンラップを行わずに安全にオプショナル型のオブジェクトを扱うことができます.上の例でも存在しないかもしれないjohn.residence.numberOfRoomsを直でアクセスせずに,residenceがnilかそうでないかによって後続の処理を変えることができます.

さらに,前述したようなオプショナル型の変数がnilであるかそうでないかによって後続の処理を分岐させるためのより工夫された仕組みがあります.次回はそれらを中心についてまとめていこうと思います.

参考

iOS, Swift