【Swift】オプショナルバインディングでネストを避ける

  • 11
    いいね
  • 1
    コメント

最近、「シャドーイング」という言葉を知ったので、覚え書きと教えてくれた人への感謝の気持ち込めて投稿します。技法自体は知っていたのですが、「シャドーイング」と呼ばれていることを知らなかったんですね。
簡単に試せるように、Playgruond上で実行できるようにコードを記述しています。

オプショナル型の変数

オプショナル型があるSwiftには、変数の名前で困ることがあります。
例えば

変数定義
var value: Int?
value = 123

とすると、この変数valueはオプショナルIntなので、nilになる可能性があります。
オプショナルであることがわかりやすい変数名にすればいいかもしれませんが、そうするとnilでない値が保証されている時に煩わしいことになります。

オプショナルバインディング

コードを記述していくと、途中でこの変数valueをアンラップする際に値がnilかどうかを常に気にしないといけないのが、面倒くさくなってきます。
そこで、オプショナルバインディング(Optional-Binding)という記述方法を使うと、「変数valueがnilでない場合」と「nilだった場合」の処理を分岐できます。

If-Let構文
let value: Int?
value = 123

if let value = value {
   // コードブロックA: valuenilでない時の処理
   type(of: value)    // ここでは普通のInt
} else {
   // コードブロックB: valuenilだった時の処理
   fatalError()
}

type(of: value)   // この時点でオプショナルInt

シャドーイング

if let value = value {

If-Let構文で、同じ名前の定数オブジェクトを使うところが「シャドーイング」です。
この時点で、変数valueは定数valueの影に隠れて見えなくなってしまうんですね。もちろんオプショナルではないことが保証されているので安全です。

If-Let構文を抜けた後では、valueはInt?型(型パラメータで表現するとOptional)に戻っています。

Guard-Let構文

同じシャドーイングのようでも、反対な使い方ができるのがGuard-Let構文です。

Guard-Let構文
let value: Int?
value = 123

guard let value = value else {
   // コードブロックC: valuenilだった時の処理
   fatalError()
}

type(of: value)   // valueは普通のInt

コードブロックCでは、valueがnilの場合に行う処理を記述します。
ここでは、必ずコードブロックを抜ける処理をしなければいけません。例えばreturn、break...のような。

注目すべきは、Guard-Let構文を抜けた後のvalueです。
この時点で、valueの型はnilでないことが保証された普通のInt型になっています。

nilチェックとして

If-Let構文とGuard-Let構文はどちらもnilチェックとして使われますが、Guard-Let構文は後発(Swift2.x~で追加)だったので、積極的には使われていない気がします。
「nilでない場合」の処理を記述したいケースを、If-Let構文だけでコーディングするとネストが深くなってしまいコードが読みづらくなってしまいます。

Guard-Let構文と早期returnを活用すると、必要以上にネストせずに安全なコードが書けるので、私のような初心者でもわかりやすいサンプルコードがたくさんできるといいなと思います。