忘備録-Swiftのクロージャ


趣味でIOSアプリ開発をかじっていた自分が、改めてSwiftを勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。いつか書き直します。

## 参考文献

この記事は以下の書籍の情報を参考にして執筆しました。

- [詳解 Swift 第5版](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3Swift-%E7%AC%AC5%E7%89%88-%E8%8D%BB%E5%8E%9F-%E5%89%9B%E5%BF%97/dp/481560407X)

##クロージャ概要


簡単なクロージャ の例


```swift
let c1 = {
  print("Hello")
  print("hoge")
}
let c2 = {
  (a:Int, b:Int) -> Double in
  return Double(a) * Double (b)
}
print("c1-------")
c1()
print("c2-------")
print(c2(10,2))
```

出力

```
c1-------
Hello
hoge
c2-------
20.0
```

##クロージャと関数の型
クロージャーは関数と同じように実行でき、同等のものとして扱うことができる。
クロージャーと関数の違いは後で書く。

```swift
let c1 = {
  (a:Int, b:Int) -> Double in
  return Double(a) * Double (b)
}

var c2: (Int, Int) -> Double = c1
print(c2(10,8))    // 80.0
print(c1)    // (Function)
func f1(a: Int, b:Int) -> Double{ return Double(a) * Double (b) }
print(f1)    // (Function)
c2 = f1
print( f1(a:2, b:6) )    // 12.0
print( c2(a:2, b:6) )    // erroe
print( c2(2, 6) )    // 12.0
```

##オーバーロードされた関数の区別
使うときに引数ラベルを指定する
関数の引数と戻り値がわかる方法で記述する。

```swift
let c1 = {
  (a:Int, b:Int) -> Double in
  return Double(a) * Double (b)
}
    
var c2: (Int, Int) -> Double = c1

func f1(a: Int, b:Int) -> Double{ return Double(a) * Double (b) }
func f1(c: Int, d:Int) -> Double{ return Double(c) / Double (d) }    // 追加
func f1(f: Int, g:Int) -> String{ return "\(f) + \(g)" }    // 追加

c2 = f1(c:d:)    // 変更 引数ラベルを指定
print( c2(2, 6) )    // 0.3333333333333333
    
//以下追加
var c3: (Int, Int) -> String
c3 = f1    // f1の中でj上記の引数と型を持つのは一つしかないので引数ラベル不要
print(c3(1,3))    // 1 + 3
```

##クロージャーの複雑な宣言

```swift
// オプショナル型を返すクロージャー
let c1: (Int, Int) -> Double?

// クロージャー自体がオプショナル型
let c2: ((Int, Int) -> Double)?


// クロージャーの配列
let c3 = [(Int, Int) -> Double]()
// クロージャをtaypaliasで宣言してコンパクトにする
// クロージャの処理用途を記載して意味のある名前にすると可読性が上がる
typealias MyClosure = (Int, Int) -> Double
let c5 = [MyClosure]()
```

####メモ
オプショナルのクロージャーってどういう状態？

```swift
let c1 = {
  (a:Int, b:Int) -> Double in
  return Double(a) * Double (b)
}

var c2: (Int, Int) -> Double
//print(c2) //error 初期化を促される
c2 = c1
print(c2)    // (Function)
    
var c3: ((Int, Int) -> Double)?
print(c3)    // nil
c3 = c1
print(c3)    // Optional((Function))
```


##変数のキャプチャ
クロージャが関数と違うのは使い方。
クロージャは常に同じ機能のインスタンスが作られるのではなく、プログラムの外側にある変数の値を取り込んでインスタンスの一部とし、インスタンスが呼び出されるときにはいつでも値を取り出して使える。これをキャプチャという。

新しくファイルを作って
クロージャを返す関数を作成する。

```swift
var globalCount = 0

func maker(_ a: Int, _ b: Int) -> (() -> Int){
  var localvar = a
  return { () -> Int in
    globalCount += 1    // globalCount+1するだけ
    localvar += b
    return localvar
  }
}
```

作った関数を使って２つのクロージャの挙動をみる。
クロージャm1とm2はそれぞれで値が増えていくので、それぞれのインスタンスで処理の外にある変数の実体を保持していることがわかる。


```swift
var m1 = maker(10, 2)
print(m1(),globalCount)    // 12 1
print(m1(),globalCount)    // 14 2
globalCount = 100
print(m1(),globalCount)    // 16 101
    
var m2 = maker(100,50)
print(m2(),globalCount)    // 150 102
print(m1(),globalCount)    // 18 103
print(m2(),globalCount)    // 200 104
```

##変数キャプチャの共有
同一の変数をキャプチャした場合変数のキャプチャが共有される

```swift
var m1:(() -> ())! = nil
var m2:(() -> ())! = nil

func makerW(_ a: Int){
  var localvar = a
  m1 = { localvar += 1; print("m1: \(localvar)")}
  m2 = { localvar += 5; print("m2: \(localvar)")}
  m1()    // m1: 11
  m2()    // m2: 16
}

makerW(10)
m1()    // m1: 17
m2()    // m2: 22
m2()    // m2: 27
m1()    // m1: 28
```

##参照型変数のキャプチャ
クロージャがクラスのインスタンスをキャプチャすると強い参照でインスタンスを保持する。

```swift
class MyInt {
  var value = 0
  init(_ v: Int) { value = v }
  deinit {print("\(value) : deinit")}
}

func makerZ(_ a:MyInt, _ s: String) -> () -> () {
  let localvar = a
  return {
    localvar.value += 1
    print("\(s) : \(localvar.value)")
  }
}

var obj = MyInt(10)
var m1:(() -> ())! = makerZ(obj, "m1")
m1()    // m1 : 11
var m2:(() -> ()) = makerZ(obj, "m2")
m2()    // m2 : 12
    
m1()    // m1 : 13
m1 = nil    // この時点でインスタンスは解放されない
m2()    // m2 : 14
// ここでインスタンスが解放される 14 : deinit
```


##キャプチャリスト
クロージャの先頭に[]で囲んだ変数はキャッチリストと呼ばれ、ここに記述した変数はクロージャの生成時に値がコピーされ元の変数の値に変更があっても影響を受けない。
ただし、コピーできるのは値型のデータで、参照型のデータはキャプチャリストを使っても同じ値を参照するので個別に値を持てない。

```swift
var a, b, c: () -> ()
do{
  var count = 0
  var name = "Hoge"
  a = { print(count, name) }
  b = { [count] in print(count, name) }
  c = { [count, name] in print(count, name) }
  count = 3
  name = "Fuga"
}
a()    // 3 Fuga
b()    // 0 Fuga
c()    // 0 Hoge
```

キャプチャリストの中の識別子はクロージャ内で別の名前を指定することも可能なので、このように記述したほうがわかりやすい。

```swift
c = { [num = count, title = name] in print(num, count) }
```

##引数リストの省略

```swift
var c: (Int, Int) -> String

// 今まで取り扱った記述
c = { (a:Int, b:Int) -> String in "\(a)\(b)" }
print(c(1,3))

// 引数と返り値の省略
c = { a, b in "\(a)\(b)" }
print(c(1,3))

// $数字という記述で何番目の仮引数か知ることができる。
c = { "\($0)\($1)" }
print(c(1,3))
```

##配列の整列
配列の要素を並び替えるメソッドsorted()は引数としてクロージャを使う

```swift
let list = ["hoge", "fuga", "piyo", "foo"]
let slist = list.sorted(by: {
  (a:String, b:String) -> Bool in a<b
})
print(slist)    // ["foo", "fuga", "hoge", "piyo"]
```

```swift
// 書き換え
let slist = list.sorted(by: {
  $0 < $1
})
//  <は次のように書くこともできる
let slist = list.sorted(by: < )
```


##クロージャを引数とする関数の定義

```swift
//渡された文字列の配列を条件に合致する配列とそうでない配列に分けて返す関数
func separate(_ list: [String], by filter:(String) -> Bool) -> ([String],[String]){
  var sel = [String]()
  var des = [String]()
  for s in list {
    if filter(s) {
      sel.append(s)

    } else {
      des.append(s)
    }
  }
  return (sel, des)
}

let list = ["Hoge", "fuga", "piyo", "Foo", "AAA", "aaa"]

let t = separate(list, by:{
  for c in $0 {
    if case "A" ... "Z" = c {return true}
  }
  return false
})
print(t)    // (["Hoge", "Foo", "AAA"], ["fuga", "piyo", "aaa"])
```

##接尾クロージャ
クロージャ式が引数の最後で呼ばれる場合、特別な記述ができる。
上記で記述した呼び出し部分の書き換え

```swift
let t = separate(list){
  for c in $0 {
    if case "A" ... "Z" = c {return true}
  }
  return false
}
print(t)    // (["Hoge", "Foo", "AAA"], ["fuga", "piyo", "aaa"])
```

##エラーを投げるクロージャ

```swift
let hoge = { (a:Double) throws -> Double in
  guard a > 0 else { throw XError.zero }
  return 1.0
}

// 使い方
if let d = try? hoge(-9.0){
  print(d)
} else {
  print("error")    // error
}
```

##再通報する関数
エラーを投げるクロージャを引数とする関数が、そのクロージャを実行した結果エラーを伝播させる場合、再通報するという。
引数リストの後にrethrowsを置く
発生したエラーを外部に伝播させる必要がないのであればrethrowsは書かなくていい

```swift
func Fuga(_ hoge: (Double) throws -> Double) rethrows -> Double{
  //処理
  let d = try hoge(-9.0)
  return d
}


let hoge = { (a:Double) throws -> Double in
  guard a > 0 else { throw XError.zero }
  return 1.0
}
do{
  let d = try Fuga(hoge)
  print(d)
}catch{
  print("error")    // error
}
```

##参照の循環
インスタンスのメンバのクロージャが他のメンバのキャプチャをしたときに参照の循環が起きる。

```swift
class Hoge {
  let str = "hoge"
  var fuga = { () -> String in " " }
  func hoge() {
    fuga = { () -> String in
      return self.str
    }
  }
  deinit {
    print("deinit")
  }
}

do {
  let piyo = Hoge()
  piyo.hoge()
}
// deinitが呼ばれない
```

##キャプチャリストを使った解決
キャプチャリストは変数のコピーを作成するので参照の循環が起きなくなる。

```swift
func hoge() {
  fuga = { [str] () -> String in
    return str
  }
}
```

##弱い参照を使った解決
弱い参照にするとunwrapする必要がある。

```swift
func hoge() {
  fuga = { [weak self] () -> String in
    guard let self = self else { return " " }
    return self.str
  }
}
```

##関数の引数にクロージャを渡す
関数の引数にクロージャを渡したあと、関数内でクロージャを変数に保存することによって、関数呼び出しが終了した後でもクロージャが呼び出される可能性がある。
このような状態をクロージャの離脱、またはエスケープという。
クロージャが保存されない場合、関数終了時にクロージャへの参照が消滅する。
クロージャが保存されると、強い参照でキャプチャしている他のインスタンスが解放されない。


```swift
var theFunc:((Int) -> Int)! = nil
func setFunc(_ f:(Int) -> Int) { theFunc = f }
```

関数の引数のクロージャが離脱する場合、@escapingで修飾しないといけない

```swift
func setFunc(_ f:@escaping (Int) -> Int) { theFunc = f }

```