[Swift] クロージャ(Closures)を理解してみよう

クロージャについてまとめたものを共有します。
環境は以下の通りです
Swift 5.1
Xcode 11.3

# クロージャの形態
{(パラメータリスト) → リターン型 `in` 実行するコード}
何もパラメーターで受け取らず、何も返さないクローザーは　`( ) → ( )` です。

# Swiftの関数はクロージャ！

Swiftの関数はクロージャです。
クロージャは「関数の本体内のスコープ内で外部変数への参照をキャプチャできること」を意味します。

例えば、下のドグのインスタンスを参照してみよう。
`whatThisDogSays`を参照できるのが分かります。
<img width="273" alt="スクリーンショット 2020-06-06 12.29.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/cac03cc9-8641-c766-6049-8426ebbd2f1a.png">

`dothis`関数には`Dog`インスタンスがないが参照により`"arf"`を呼び出します。
Dog 変数 `d` と関数 `barkFunction` の両方が、同じ`Dog`インスタンスへの参照を保持しているからです。
<img width="229" alt="スクリーンショット 2020-06-06 12.29.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/ba0143c3-c48a-9b2d-71e9-9b54acc62161.png">


# クロージャがコードをどのように改善するか
作ったクロージャコードを簡略化にして、より便利に使うことができます。
<img width="528" alt="スクリーンショット 2020-06-06 13.17.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/cb0f088c-6245-e10e-1a7b-0f22ff8e1de5.png">
上の関数はイメージを作るtrailing closureです。
<br>

<img width="610" alt="スクリーンショット 2020-06-06 13.20.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/79e119af-2643-6d1f-e420-e56bf02fc8e7.png">
このような呼び出すことできるので便利です。
<br>

<img width="592" alt="スクリーンショット 2020-06-06 13.21.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/347ee1ea-35eb-466e-065c-f0e7e65df7ab.png">
繰り返されるコーディングが嫌ならこのように書き換えます。
<br>

<img width="615" alt="スクリーンショット 2020-06-06 13.23.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/0dc403f2-f7c8-e38a-ce10-5ae4404a877a.png">
ハードコーディングがいやならこのように修正するのもよいでしょう。
<br>

<img width="471" alt="スクリーンショット 2020-06-06 13.25.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/f3698703-e79c-22bc-feec-ca428c575bc6.png">
上の関数はこのように呼び出すことができます。

# 関数をりタンする関数

上の例ではイメージをリターンするが、今回は関数をリターンしてみよう。
関数から値として返された関数を見たことがない方は息が詰まるかもしれないが、とりあえずゆっくりみてみよう。
<img width="640" alt="スクリーンショット 2020-06-06 13.53.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/630b3191-1154-64b4-4ee1-2ffe207ae6a0.png">
下線を引いた部分はリターンされる関数です。
関数をリターンするために fという関数を作りました。
`f` は `im` でリターンされているイメージだけを持ってますし、それをリターンします。
<br>

<img width="487" alt="スクリーンショット 2020-06-06 13.54.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/6fa3cd4c-996c-b2cd-b25c-44066012e00a.png">
上の関数を変数に保存すると変数は関数の形になります(関数が関数をリターンするため)。
こうなると、普通の関数を呼び出すことができるので便利になります。
（`makeRoundedRectangle`関数はファクトリーのように使える）

<img width="651" alt="スクリーンショット 2020-06-06 17.48.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/89c3a2ff-4ef0-1833-2187-2012efe1af3c.png">
上の関数はよりSwift化した形です。変数`im`は必要ないため省略し、関数名の前に`return`をつけます。
<br>

<img width="644" alt="スクリーンショット 2020-06-06 17.51.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/d563dde4-998f-e716-6f7d-c440386d077c.png">
上の関数をよりSwift化した形です。関数名`f`も必要ないです。
<br>

<img width="648" alt="スクリーンショット 2020-06-06 17.51.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/a25f6f99-9ea3-6b71-647d-b7841cb412e9.png">
リターンされる関数前の`return`も省略可能です。

# キャプチャされた変数を設定(set)します。

クロージャは変数を設定(set)できます。
<img width="267" alt="スクリーンショット 2020-06-06 18.10.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/e4dcdd05-cc8b-85c8-b79b-d514e6972e79.png">

簡単な例です。


# キャプチャした環境を保存するクロージャ

<img width="416" alt="スクリーンショット 2020-06-06 18.54.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/5965f4fa-bd54-585a-2b56-e86f7c35682f.png">

ある関数は直接呼び出されるのではなく後で実行するため、その関数が保存される（その環境をとらえ、時間の経過とともに保存する）クロージャを`escaping closure`と言います。(上のように`@escaping`をつきます。）

<img width="280" alt="スクリーンショット 2020-06-06 18.54.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588409/d557b4a6-6a41-9f5e-fa64-2e01062c4026.png">

次のような`countedGreet()`を呼びだすとカウントされた値が出力されます。
一体どこで変数をカウントするでしょうか。
変数`count`はローカル関数（匿名関数）で宣言されてないですが、それを利用して０に初期化され、後にカウントしています。
もし、`count`が匿名関数内で宣言された場合、`countedGreet()`が呼び出されるたびに `ct`を0に設定します。
それは、`countedGreet()`が呼び出されるたびにインクリメントするように、ある親密的な環境に`countedGreet()`が保持されるからです。

# 参考
iOS13 Programming Fundamentals with Swift, Matt Neuburg(2019) p50 - 58