swift

【Optional型】アンラップの仕方や非Optional型との違い

Swiftで開発する上で一つのポイントとなる、Optionalの扱い方についてです

前置き

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

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

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

Optionalと非Optionalの決定的な違い

答えは次の通り。非常にシンプルです。

  • optionalはnilを代入できる
  • 非optionalはnilを代入できない

そもそも「nil」とはどういう状態なのか??

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

nil or not nil
「0」と「nil」は異なる。左が「0」。右が「nil

Optionalの変数には初期値が要らない

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

var string: String = "ああああ" //OK
var string: String //エラー: 初期値がない

それに対してoptionalはnilを代入できるので必ずしも初期値は必要ありません。

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

Optional値の扱い方

さらに詳しくみていきます。Optionalと非Optional両方の変数を定義し、どちらも同じ文字列"ああああ"を代入後、その値をprintしてみます。

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

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

実行結果が異なることがわかります。これは困ったことになりました。例えば”2020”と表示したいのに”Optional(2020)“と表示されてはOptional型は使い物になりません。

Optionalから非Optional型への変換

ではString?Stringへの変換はどのように行うと良いでしょうか?Optional型の値を通常の値に変換する作業をアンラップ(unwrap)と呼びます。

アンラップの由来は包み(wrap)をほどく(un)ことから来ています。例えばOptional("ああああ")からOptional()という包みを取ると"あああ"という文字列が残ります。

アンラップの方法

このアンラップにはいくつかの方法があります。よく使うのは次の3つです

  • !(強制アンラップ)
  • if let構文
  • guard let構文

順番に説明していきます

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

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

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

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

この現象を防ぐ方法としてアンラップする前に値がnilであるかどうかをチェックすることが挙げられます。

if optinalString != nil {
   print(optinalString!) //nilの場合は実行されない
}

事前にif文でチェックすることで、値がnilだった場合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構文はメソッドのスコープ内でのみ使用できます。ある変数の値がnilの時は処理を中断したり、早期で別の値をreturnしたい場合などは積極的に使っていきましょう。

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)"

optionalString = nil
print(optionalString?.count) //実行結果: nil

??

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

let hoge = (A ?? B)

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

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

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

func check(value : String?) {
    print(value ?? "nilだよー")
}

(value ?? nilだよー")をprintしているところに注目です。呼び出しと実行結果はこうなります。

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

まとめ

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

最後に

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

読んでいただきありがとうございます!!

記事として取り上げたトピックを体系化してまとめた内容を電子書籍として販売しています。購入していただくことで執筆の応援ができます。詳細はこちらから