【Swift】@escapingや@autoclosureについて

# これは？
Swiftのアノテーションである **@ escaping** と **@ autoclosure** がどんな役割を果たすのかについてまとめました。

# @ escaping
Swiftのクロージャは参照型であるため、少し扱いには注意が必要です。

例えば、非同期処理を行う関数を宣言し、引数にクロージャの **completionHandler** (非同期処理の完了時に実行する処理)を取るとしましょう。

completionHandler は非同期処理が完了するまで待機しなければなりませんが、その前に関数のスコープを抜けると引数であるクロージャは解放されてしまいます。

それを防ぐために *関数のスコープから逃げる* 、これが **@ escaping** です。

以下のように非同期処理の後にクロージャを実行する場合は、**@ escaping** を引数で受け取るクロージャに付与しなければなりません。

```swift
class AsyncClass {
    func syncMethod(completionHandler: () -> Void) {
        print("sync")
        completionHandler()
    }
     
    func asyncMethod(completionHandler: @escaping () -> Void) {
        print("async")
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            completionHandler()
        }
    }
}
 
class TestClass {
    var tmp = 0
 
    let asyncInstance = AsyncClass()
    //selfを書かなくても良い
    func syncTest() {
        asyncInstance.syncMethod {
            tmp = 100
        }
    }
    //selfを明示的に書かなければならない
    func asyncTest() {
        asyncInstance.asyncMethod {
            self.tmp = 100
        }
    }
 
    deinit {
        print("released")
    }
 
}
```

**@ escape** でアノテートしているクロージャの場合、selfのキャプチャを明示的に示す必要があります。
この時に注意しなければならないのが、**循環参照** というやつです。

試しに以下の処理を加えて見てください。

```swift
do {
    let testInstance = TestClass()
    testInstance.asyncTest()
}
```

doスコープを抜けるとき、本来なら `testInstance` は解放され、 `TestClass` の `deinit` が呼び出されるはずですが、 *”released”* という出力は確認できません。

これは、

1. `AsyncClass` の `asyncMethod` メソッドに引数として渡したクロージャがselfを強参照
2. そのクロージャを `AsyncClass` のインスタンスが強参照
3. selfもまた `AsyncClass` のインスタンスを強参照

しているため、 **循環参照** が完成しているのです。

解決するために、クロージャの中のselfは **弱参照** にしてあげましょう。

```swift
func asyncTest() {
    asyncInstance.asncMethod { [weak self] in
        self?.tmp = 100
    }
}
```

# @ autoclosure

**@ autoclosure** は関数に渡された引数をクロージャにラップします。

なので、そのクロージャは原則引数は持ちません。
少しイメージがつきづらいと思うので、例を示します。

```swift
var customerInLine = ["Mike", "Jon", "Bob"]
 
func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())")
}

serve(customer: { customerInLine.remove(at: 0)} )
```

順番待ちをするお客の配列 `customerInLine` と、先頭のお客にサービスを提供する `serve(customer:)` 関数です。

ここで、serve関数の引数にわざわざクロージャを渡すのは煩わしいですね。
そんな時に役立つのが **@ autoclosure** です。

```swift
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())")
}

serve(customer: customerInLine.remove(at: 0))
```

このように、 `customerInLine.remove(at: 0)` から、戻り値の `String` を受け取るのではなく、クロージャとしてその処理を引数に取ることができるのです。

この **@ autoclosure** は以下のようなことを可能とします。

```swift
var customerInLine = ["Mike", "Jon", "Bob"]
 
var isBusy = true
 
func serve(customer customerProvider: @autoclosure () -> String) {
    if isBusy {
        print("Now busy, so please wait")
    } else {
        print("Now serving \(customerProvider())")
    }
}
 
serve(customer: customerInLine.remove(at: 0))
print(customerInLine.count)
```

現在が忙しいならばお客に待ってもらうように処理を加えました。

これにより、 `customerInLine.remove(at: 0)` の処理を実行せずに済みます。
`serve` を呼び出した後もお客の数が `3` のままであることがその証拠です。

**@ autoclosure** は、引数として受け取る処理を遅延あるいは飛ばす可能性がある場合に用いると便利そうですね。
ただ、可読性の点から見ると使いすぎるのも良くなさそうです。

# 参考
[Apple Inc. “The Swift Programming Language" -Closures](https://docs.swift.org/swift-book/LanguageGuide/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID94)