swiftにおいて、!
と?
がよく理解されていないようなので、まとめておきます。
結論から先に言うと、!
と?
には、それぞれ下記の意味があります。
!
→ 『Forced unwrapping』という機能
→ 『Implicitly Unwrapped Optional』という型を示す
?
→ 『optional chaining』という機能
→ 『optional型』という型を示す
単語だけ見ても意味が分からないかもしれませんが、これから順番に説明していきますのでご安心を。
しかしながら前提として、知っておかなければならない知識があるので、先に教えておきます。
- 型にはoptionalなものとそうじゃないものがある
Swiftではnilを許容するかしないかで明確に型を分けています。
nilを許容しないものは「String」や「Int」などの普通の型で定義し、nilを許容するものは「OptionalなString型」や「OpionalなInt型」で定義します。
- optionalな型とoptionalではない型はクラスが異なる
optionalな型とoptionalではない型はクラスが異なっており、使用可能なプロパティやメソッドが異なります。
たとえば、String型の変数はlowercaseString
というプロパティを持ちますが、optionalなString型の変数はそのプロパティを持ちません。
そのため、同じStringという文字がついている型だからといって同じように扱うことはできません。
以上が前提です。
では、説明に入っていきます。
!
『Forced unwrapping』という機能
先の説明で、Optionalな型とそうではない型は別物で扱う必要があるといいましたが、nilの許容の有無でいつも使っているようなプロパティが使えなくなるのは困りますよね?
たとえば、下記のようにするとエラーが起きます。
let str: Optional<String> = "HOGE"
println(str.lowercaseString) // →エラー
そのため、Swiftには上記を解消するための仕組みが2つ備わっています。
1つめが、この『Forced unwrapping』です。
Optionalな変数に!を付けることで元の型に変換することができます。
サンプルを示します。
let str: Optional<String> = "HOGE"
println(str!.lowercaseString) // →エラーが起きずにhogeと表示される
これは、OptionalなString型であったstr
が!
によってString型に変換されたことを意味しています(ちなみに、元の型のメンバを使えるようにすることを「アンラップ」と言います)。
2つ目の仕組みに関しては後述の『optional chaining』のところで説明します。
『Implicitly Unwrapped Optional』という型を示す
上記で、Optionalな型の場合!
を付ければ、元の型のメンバが使えるようになりましたが、長いソース書いていたりすると、使おうとしている変数がオプショナルかどうかがわからなくなってきたりします。
そのため、!
を付け忘れてエラーといったことが起きてしまします。
そこで、それを解決するために、『Implicitly Unwrapped Optional』といった型が準備されています。
これは、元の型のメンバを使用しようとしたときに、!
をつけていなくても、自動的に元の型にアンラップしてくれる機能を持っている型です。
サンプルを示します。
let str: ImplicitlyUnwrappedOptional<String> = "HOGE"
println(str.lowercaseString) // →エラーが起きない
このように、ImplicitlyUnwrappedOptional型
で変数を宣言することによって、!
をつけなくても自動的にアンラップしてくれるようになります。
ちなみに、下記のようなエイリアスもあります。
let str: String! = "HOGE" // →びっくりマークを付ける
println(str.lowercaseString)
?
『optional chaining』という機能
先の『Forced unwrapping』の説明の際に、nilの許容によって元の型にあるメンバが使えなくなる問題を解消するための仕組みが2つあるとお伝えしましたが、その2つ目の仕組みが『optional chaining』です。
『optional chaining』も、『Forced unwrapping』と同様、元の型のメンバが使えるようになります。
サンプルを示します。
let str: Optional<String> = "HOGE"
println(str?.lowercaseString) // →エラーが起きずにhogeと表示される(※正確には、Optional("hoge") といったような表示になる)
『Forced unwrapping』と異なるのは、注意文言を見てわかったかも知れませんが、アンラップされても変数がもとのOptional型であり続ける点です。
『Forced unwrapping』のときはString型になりましたが、『optional chaining』のときはOptionalなString型のままです。
このように、Optionalな変数に?
を付けることでエラーが起きずに元の型のプロパティを参照することができます。
まとめると、
アンラップの方法 | 記号文字 |
---|---|
Forced unwrapping | ! |
optional chaining | ? |
です。
『optional型』という型を示す
これまで、Optionalな変数を宣言するときは下記のように記載してきました。
let str: Optional<String> = "HOGE"
しかし、実は、もっと簡単な記載方法があります。
let str: String? = "HOGE"
これです。
こちらの方が一般的かと思います。
まとめ
以上をまとめると、下記のようになります。
// 『Forced unwrapping』
let str: Optional<String> = "HOGE"
println(str!.lowercaseString) // →変数に!をつける。型は変わる
// 『Implicitly Unwrapped Optional』①
let str: ImplicitlyUnwrappedOptional<String> = "HOGE"
println(str.lowercaseString) // →ImplicitlyUnwrappedOptionalで宣言することで、自動的にアンラップされる & 型は変わる
// 『Implicitly Unwrapped Optional』②
let str: String! = "HOGE" // 『ImplicitlyUnwrappedOptional』のエイリアスver
println(str.lowercaseString)
// 『optional chaining』
let str: Optional<String> = "HOGE"
println(str?.lowercaseString) // →変数に?をつける。型は変わらない
// 『optional型』
let str: String? = "HOGE" // →オプショナル変数であることを宣言する
以上、?
と!
についてのまとめでした!
何かわからないことなどありましたらお気軽にご連絡ください~。