[Swift]  Swiftのoptional valueの便利さ /「?」と「!」でより堅牢なコードへ


この記事ではSwiftの`optional value`の挙動と、利用することのメリット、具体的な利用シーンについてまとめてみました。

## 基本的な挙動について

まず`?`と`!`の挙動についてまとめてみます。 実際にどのようなケース利用するかの使い分けや、使うことのメリットについては次の章で説明します。 

挙動の説明を見て、わかりづらいなと思ったら、この章は流し読みして次章の「具体的な利用シーン」とこの章を交互にみるとわかりやすいかもしれません。

### optional valueな変数の宣言

Swiftでは、ただ宣言しただけの変数には`nil`を代入することができません。
例えば以下のようなコードは静的にエラーになります。

```ts
var optionalValue:String = "HelloSwift"
optionalValue = nil  //この行で静的解析時にエラー
```

以下のように宣言の際に型の後ろに`?`をつけることで`nil`が代入可能な変数になります。

```ts
var optionalValue:String? = "HelloSwift"
optionalValue = nil  // 正常に代入できる
```

### !と?を利用したOptionalChain

```ts
var optionalString:String? = "HelloSwift"
println(optionalValue)  // この行で静的解析時にエラー
```

optionalValueな変数の末尾には`?`か`!`の、いずれかを必ずつける必要があります。
２つの記号は変数に`nil`が入っている場合に挙動が変わります。 
`!`を付けた場合は変数の中身が`nil`だと実行時にエラーになります。
`?`を付けた場合は変数の中身が`nil`だとエラーにはなりません。

```ts
// 変数がnilの場合は、?か!で挙動が違う
var hoge:String?  // 初期値はnil
println(hoge?) // hogeがnilなのでエラーにはならない
println(hoge!) // hogeがnilなので実行時エラー

// 変数に中身が入っている場合は?でも!でも挙動は同じ
var fuga:String? = "FugaFuga"
println(fuga?) // "FugaFuga"
println(fuga!) // "FugaFuga"
```



エラーが起きるのは静的解析時ではなく実行時であることに注意してください。
なぜなら変数の中身は実行時のコンテキストによって変わるからです。

また`nil`に対して`method`が呼ばれた場合結果は`nil`となります。
optional valueに対して`method`を利用してみましょう。考え方は先ほどと同じです。

```ts
// 変数がnilの場合は、?か!で挙動が違う
var hoge:String?  // 初期値はnil
println(hoge?.uppercaseString) // hogeがnilなのでmethodの返り値もnil
println(hoge!.uppercaseString) // hogeがnilなので実行時エラー

// 変数に中身が入っている場合は?でも!でも挙動は同じ
var fuga:String? = "HogeHoge"
println(fuga?.uppercaseString) //FUGAFUGA
println(fuga!.uppercaseString) //FUGAFUGA

```


MethodをChainで重ねて書いた場合も、上記と考え方は同じです。

```ts
// とあるオブジェクトuserがoptional valueで
// さらにuserが所持しているプロパティprofileもoptional valueの場合のコード

var user:User?

// 以下はuserがnil、もしくはprofileがnilなら実行結果はnil
user?.profile?.sex

//以下はuserがnilなら実行時エラー /profileがnilなら結果がnil
user!.profile?.sex

//以下はuserもprofileも値が入っていなければ実行時エラー
user!.profile!.sex
```

##  何が便利で嬉しいのか?具体的な利用シーンから考える

### 状況の想定

具体的な状況を想像してみましょう。
このQiitaのようなサービス中のコードで１つの記事を表現する`Article`というクラスが存在し、以下のようなpropertyを持っているとしましょう。

- `Article#id`
 - 記事を識別するための文字列で必ず存在する仕様
- `Article#body`
 - 記事の本文が入る文字列で必ず存在する仕様
- `Article#category`:
 - 記事のカテゴリを表す文字列でオプショナル

これをSwiftのクラスで書くと以下のようなコードになるでしょう。

```ts
// 投稿記事を表現するArticleクラス
class Article {
    var id       : String
    var body     : String
    var category : String? // optional value
    init(id: String, body: String, category :String?){
        self.id       = id
        self.body     = body
        self.category = category
    }
}
```

さらに、記事(Article)のidを指定するとDBからArticleのインスタンスを取得する関数`getArticleById`が存在していると想定します。
とりあえず実行できるように仮で中身を書いておきますが、呼び出すときは脳内でDBから取得してるような気分で読んでみてください

```ts
func getArticleById(id:Int) -> Article?{
    
    // 本来はここにDBから取得する処理が入りますが
    // 暫定的にあるID(1234)の記事だけがDBに存在するかのように振る舞うようにしておきます
    
    let existentId:Int = 1234 // このIDの記事しかDBに存在しないとする
    
    // IDが1234の場合のみArticleのインスタンスを返し,それ以外はnilを返す
    return id == existentId //三項演算子
        ? Article(
            id: existentId,
            body: "すごい本文",
            category: "Swift"
            )
        : nil
}
```

## 上記のコードの利用者になる

突然ですが、あなたは上記のコードを書いた直後に頭を打って中身を忘れたとしましょう。
しかしインタフェースだけは覚えています。もしくはインタフェースも忘れたけど補完を駆使してあなたはがんばります。
( 実際に頭を打たなくても、そんなことはよくありますね。また他人が書いたコードを利用して同じような状況になることはもっとあるでしょう。 )

さっそく、あなたは記事を表示する画面の開発に入ります。

###`?`が活きる場面

あなたは`getArticleById`の詳細を忘れ、`nil`が帰ってくることがあるということを忘れたまま
以下のようなコードを書いたとしましょう。

```ts
var article:Article = getArticleById(1234) // 静的解析でエラー
```

すると、あなたの利用しているIDEに警告がでます。　警告を見てあなたは気づきます。
「`getArticleById`の返り値は`optional value`だ。　」
そして思い出します。
「 そういえば`getArticleById`は無効なIDを指定した時には`nil`を返し、存在するIDを指定した時のみ`Article`のインスンタスを返す仕様だった」と。

**静的解析による警告のみ**で気づけたことに注目してください。 **コードを一度も実行せず**に潜在的な問題に気づくことができました。

もし使っている言語が`Java`のように`NULL`にmessageを送った時にエラーを起こす言語であれば、存在しないIDの画面を開くまで気づかなかったかもしれません。
もしくは`Objective-C`のように`nil`に透過的にmessageを送れる言語だった場合、存在しないIDの画面を画面を開くと、なぜか画面が崩れていて、なぜ崩れているか、すぐにわからないかもしれません。

実行することなく問題に素早く気づけたあなたは以下のようなコードを書きます。

```ts
if let article = getArticleById(1234) {
    println(article.body) 
} else {
    // 本来はここに「存在しないIDです」という画面を描画
    println("存在しないID")
}

```
#### tips1 if文にoptional valueに渡した時の真偽判定
`getArticleById`の返り値は`optinal value`ですが上記の`if let article = getArticleById(1234)`の後ろには`?`も`!`ついていません。
これは`if`の条件に`optional value`を渡した時の特殊な書き方です。 if文の条件が`optional value`だった場合、値が入っている場合に`true`、値が`nil`の場合に`false`になります。
例えば`var booleanValue Bool?`という変数があった場合、`if booleanValue`という条件文は`booleanValue`の中身の場合は`中身が入っているのでtrue`という判定になります。 `booleanValue`が`nil`の場合のみ`false`となります。
`nil`もしくは`false`のときにif文が偽を返すようにしたいのであれば明示的に`!`を使って`if booleanValue!`のように書きます。( `!`で`optional value`を通常の値に変換しているのです )

#### tips2 if文にoptional valueに渡した時のBlockの中身
またif文の中のブロックの`article.body`のarticleの後ろにも`?`も`!`つけていませんが、エラーになりません。
なぜなら そのコードは`if let article = getArticleById(1234)`というブロックにあるので`nil`ではないことが自明なため
そのブロックの中では強制的に`optional value`ではなく通常の値に変換されています。=`optional value`が`unwrapp`された )

#### それではコードに戻りましょう

さて、あなたは引き続きコードを書いていきます。
そうだ、記事を描画するサブルーチン`layoutArticle`を作ろう、と思いつきます。
あなたは、またもや詳細な仕様を忘れ`article#category`がオプショナルな仕様であることを忘れてしまっているとしましょう。

さっそく以下のような定義をしてみます。

```ts
func layoutArticle(body:String , category:String) {
   /* 記事を描画するクールに処理 */
}
```

できたので先ほどのif文の中で呼び出してみます。

```:ts
layoutArticle( article.body, article.category) // 静的解析でエラー
```

エラーが起きてしまいました。
そして、あなたは思い出します。 article#categoryはオプショナルな仕様であって、値がはいってないことがあると。
今回も ***コードを実行する前*** に ***静的な解析で*** 忘れていた仕様を思い出すことができました。

以下のように`layoutArticle`を修正します。

```ts
// 引数categoryをoptional valueに修正
func layoutArticle(body:String , category:String?) {
   /* categoryが存在する場合としない場合で分岐させる */
}
```


### `!`を使う場面

例えば`article#category`を表示する際の仕様として、カテゴリが入力されていない場合は`なし`と出す仕様にするとします。
さらに`layoutArticle`を呼ぶ時点で、カテゴリがある場合はカテゴリ名が、ないばあいは`なし`という文字列が既にはいっているとします。

つまり`layoutArticle`からすると引数で受け取る時点でcategoryは`nil`ではないことが確定しています。
なので以下のように定義を戻します。

```ts
func layoutArticle(body:String , category:String) {
    /* categoryは存在する前提で処理 */
}
```

一方layoutArticleを呼ぶ側のコードは以下の様に修正します。

```ts
if let article = getArticleById(1234) {
    // categoryがnilの場合は明示的に"なし"を入れる
    article.category = article.category ?? "なし"

    // article.categoryは必ず入っているので!をつけてoptional valueから通常値にunwrapする
    layoutArticle( article.body, article.category!)
} 
```
`??`演算子は`Optional`の中身が`nil`の時のみオプショナルでラップされた右の値が代入されます。
なので上の例ではcategoryの中身が`nil`の場合は`Optional("なし")`、値が入っていた場合は何も変化しません。

ちょっと、強引なコードになってしまいましたね。
イメージは伝わったでしょうか？
ただ上記の例は`!`の活用というよりは、消極的に使ってる感じですね。

`!`があるおかげで助かったと言えるような場面も考えてみましょう。

### `!`が活きる場面

例えばModel層に近いようなところで、 Articleの`category`が更新されたことをシステム全体に通知するpub/sub的な関数を作ったとします。
( 実際は、自分で作らずライブラリ等、利用するかもですが。仮定として…)
そして「カテゴリ編集時間」を管理しているDBがあり、そちらはcategoryの編集をobserveしています。

```ts
func notifyUpdateCategory(artice:Article){
   // この関数を実行すると、システム全体に通知する
   // 例えばDB上に「カテゴリ編集時間」を更新する処理がある
}
```

設計する際に、あなたは`Article#category`がオプショナルなのは知っているが`nil`かどうかチェックするのは、この関数の責務ではなく呼ぶ側がチェックすべきだと考えたとします。

```ts
func notifyUpdateCategory(artice:Article){
   // この関数を実行すると、システム全体に通知する
   // 例えばDB上に「カテゴリ編集時間」を更新する処理がある
   _doNotify(NotifyTarget.Global, article.category)
}
```

上のコードを書いた時点で静的解析でエラーになります。
`article.category`は`optional value`であるため末尾に`?`か`!`かのいずれかを指定しなければいけません。

あなたは`observer`達が値が存在することを前提としていること、副作用がある可能性があることを知っているので`!`をつけることを選びます。

ここで ***?か!のどちらかを選ばなければコンパイル、実行すらできなかった*** ことに注目してください。

もし、この`notifyUpdateCategory`を利用する人が、categoryの`nil`チェックを自分の責務と思わずに、`nil`のまま渡してしまったらどうなるでしょうか？

このコードは`!`が入っているため, 実行時にcategoryが`nil`だった場合、その時点で実行時エラーとなり、observer達がされることはありません。

もし`optional value`が存在しない言語であれば、関数の提供側、利用側、どちらもが`nil`チェックは自分の責務ではないと思い込み、何らかの障害に繋がるかもしれません。

他の言語の例で考えてみます。 
`nil`にメッセージを送ってもエラーにならない`Objective-C`の場合、本来実行されてはマズイ`observer`が実行されてしまう可能性があります。

`NULL`のメッセージを送るとエラーになるJavaの場合は実行すればエラーになっていたので問題はなかったかもしれません。
ただコードを書いている時点では、そのことを意識できていませんでした。


Swiftでは`nil`に対して`!`でエラーにするのか`?`で通すのか ***意思決定しなければコンパイルが通らない***  のです。

## まとめ
Swiftでは言語自体に`optional value`という概念があることで、開発者自身がコードを実行する前に`nil`の状態を強制的に意識させられます。

これにより`nil`が存在することで起きがちだった凡ミス、コミュニケーションミスなどを防ぐ、もしくは素早く気づくことができるのではないでしょうか。

## (おまけ)Groovyのsafe navigationとの比較

`Groovy`がAndoridで開発可能になったことから、`Groovy`と`Swift`との比較記事が出ています。
その中で`Groovy`の`safe navigation`と、`Swift`の`optional value`を同機能のように紹介しているものがあったのが気になったので、追記しておきます。

`Groovy`(やJava)では`NULL`に対してメソッドを実行しようとすると実行時にエラーとなります。
そのため、例えば`user.profile.sex`のような構造があった時、`user`や`profile`に`null`が入っている可能性があるときに`sex`をとりだそうとすると愚直に書くと以下のようになります。

```groovy
if (user != null && user.profile != null ) {
    println user.profile.sex
}
```

`groovy`の`safe navigation`変数の後に`?`があった場合、その前の値が`null`であればメソッドを実行せず`null`を返します。
これを利用すると最初のコードを以下のようにスマートに書くことが出来ます。

```Groovy
println user?.profile?.sex
```

このように`Groovy`の`safe navigation`はコードを堅牢にするという意味よりは、nullチェックをより、スマートに書くための記法という意味合いが強いのではないでしょうか。

ちなみに`Objective-C`では、`nil`に対して`method`を叩くと`nil`が返ってくる仕様でした。
そのため以下のGoorvyのコードと、Objective-Cのコードは同じような挙動になります

```groovy
// Groovy
user?.profile?.sex  // ?でnull でもエラーにはならない
```

```objc
// Objective-C
[[user profile] sex]  // objCでは元々nilにmessageを送ってもエラーにならない
```

そしてGroovyの`?`を付けない場合の挙動は`Swift`の`!`をつけた場合の挙動と同じになります。

```groovy
// Groovy
user.profile.sex  // userかprofileがnullだった場合エラーになる
```

```swif
// Swift
user!.profile!.sex  // optional valueに!をつけた場合のみnil値がエラーになる
```