SwiftのOptional型について。アンラップの仕方や他の型との違いを解説

前置き

今回は文字列を扱うString型を例に考えてみます。String型を定義する時はStringと宣言します。それに対して、OptionalのString型を定義したい時はString?とすることで宣言できます。

var string: String = "Stringだ"  
var optionalString: String? = "Optional型のStringだ"  

ここまでは良いでしょう。問題は「結局、optional型と非optional型は何が違うの?」ということではないでしょうか

決定的な違いは「nilを許容するか否か」

  • 非optionalはnilを許容しません
  • optionalはnilを許容します

「nil」は中身が無である状態

nilとは「そもそも値が入っていない状態のこと」を指します。本筋から逸れるためここでは詳しく解説しませんが、nilの状態について直感的に理解したい方のためにそこそこ有名な画像があるので紹介しておきます。

\
左のトイレットペーパーが「0」。右がnil

Optionalは初期値が要らない

先ほどのnilの説明を踏まえて、そもそも非optional型の変数や定数を定義するとき下のように必ず初期値が必要でした。なぜなら初期値がなくては定義した段階で値がnilになってしまうからです

var string: String = "ああああ"  
var string: String //エラー  

それに対してoptionalはnilを許容するので、初期値は必要ありません。

var optionalString: String? = "ああああ"  
var optionalString: String?  

さらに詳しくみてみる

さらにOptionalと非Optional両方の値をprintしてみます。

var string: String = "ああああ"  
print(string) //実行結果: "ああああ"  

var optionalString: String? = "ああああ"  
print(optinalString) //実行結果: "Optional(ああああ)"  

実行結果が異なることがわかります。さらに

var string: String = "ああああ"  
var optionalString: String? = "ああああ"  
string = optionalString //エラー  

Optional型を通常の型と同様に扱うためにはアンラップ(unwrapp)をする必要があるというところがポイントであると同時に、ややこしく感じてしまうところです。

optionalから普通の型への変換(アンラップ)

ではString?Stringへの変換はどのように行うと良いでしょうか?

アンラップ(unwrapp)の仕方

よく使うのは次の3つです

  • !(強制アンラップ)
  • Optinoal Binding(オプショナル拘束)
    • if let構文
    • guard let構文

1. !(強制アンラップ)

シンプルかつわかりやすい方法がこの方法です。アンラップする対象の末尾に!を付けるとアンラップができます。どうでもいいですが英名では「Unconditional Unwrapping(無条件アンラップ)」と言います

var optionalStr: String? = "ああ"  
print(otionalStr!) //実行結果: "ああ"  

一見簡単で使いやすそうに見えますが、この強制アンラップには重大な欠点があります。それはoptionalの値がnilの場合もアンラップし、エラーを起こしてしまうことです。Optionalでしか扱えないnilを強制的に通常の型へと変換しようとするわけですから、当然エラーとなるわけです。これを防ぐためにはアンラップする前にnilであるかどうかをチェックする必要があります。

if optinalString != nil {  
   print(optinalString!) //安心してアンラップできる  
}  

このようにnilであるかどうかをアンラップする前にif文でチェックすることで、print(optinalString!)はエラーにはなり得なず、安心して使えるわけです。一概に「強制アンラップは危険!!」と考えるのではなくこのような捉え方をするとうまく扱うことができそうです。

「ここでエラーが出るはずはない」というのをちゃんと検討した上で正しく使う ! は、危険信号ではなくて、前提条件を表す強い意思表示になる\
引用: Swiftの ! は危険信号か?

2. if let構文

Swiftには先ほどの強制アンラップの説明で触れた事前のnillチェックをスマートに行うことができる構文が用意されています。それがIf let構文です。

var optional: String? = "オプショナル"  

if let unwrapped = optional {  
    print(unwrapped) //実行結果: "オプショナル"  
}  
print(optional) //実行結果: Optional("オプショナル")  
print(unwrapped) //ifのスコープ外なのでundefinedエラー  

上記のコードを例にとると変数optionalの値がnilでない場合、その値をString型の定数unwrappedとして扱うことができます。これはif letのスコープ内でのみ適応されます。 スコープ内でのみoptionalをアンラップして扱えることから、この構文は「Optional Binding(オプショナル拘束)」とも呼ばれます。 また使う機会は多くないですがif letに対して、変数バージョンのif varもあります。

3. guard let構文

これもif let構文と同じ「Optional Binding」と呼ばれるものです。guard let構文はメソッドの中でき、のみ使用できます。メソッドの中で早期リターンしたい場合は積極的に使うと良いでしょう。

var optionalNil = Optional(nil)  
var notNil = Optional(5)  

func check(optionalString: String?) -> String {  
    guard let string = optionalString else { return "nilだよ" }  
    return string + "はnilじゃないよ"  
}  
print(check(nil))  //実行結果: nilだよ  
print(check(notNil))  //実行結果: nilじゃないよ  

補足 そのほかのアンラップ

頻出 Optional Chaining

よく見ると思います。ちなみに「Chainning」を直訳すると「連鎖」と言う意味になります。メソッドは呼び出されず、nilが返されます。

var optionalString: String? = "ああああ"  
print(optionalString?.count) //実行結果: "Optional(4)"  

?? Nil Coalescing

??はSwiftで用意されている演算子の1つで、アンラップに使います。

let hoge = (A ?? B)  //この時のAはOptional  

上記のコードでは、まずAがnilであるかを評価し、次のような処理を行います。

  • Aがnilでない時、hogeの値はA!
  • Aがnilの時、hogeの値はB

具体的な例として次のようなメソッドを考えてみましょう。

func check(value : String?) {  
    print(value ?? "いや、nilやん。")  
}  

(value ?? "いや、nilやん。")をprintしているところに注目です。こうなります。

check(value: Optional("aaa")) //実行結果: "aaaa"  
check(value: nil) //実行結果: "いや、nilやん。"  

まとめ

  • optional型を通常の型に変換することをアンラップと言う
  • nilをアンラップするとエラーが起きる。事前にnilをチェックすることでこれを回避し、安全にアンラップができる。
  • 安全なアンラップは「Optoinal Binding」等、Swiftの構文として数種類用意されているので状況に応じてうまく使い分けて活用する

最後に

optionalのアンラップがわかればiOSアプリ開発の基本、コードで行う画面遷移についての理解もしやすいはずです。\
また、Optionalについてさらに極めたい人は下の記事が参考になるでしょう。