함수의 인자로 전달됐다가 함수의 리턴 이후 실행되는 클로저를 escape했다고 말한다.
그래서 함수 매개변수에 클로저가 들어갈 때, 얘가 escape 할 수 있다는 것을 의미하기 위해 매개변수 타입 앞에다가 @escape annotation을 쓸 수 있다.
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
여기서 @escaping을 통해 escaping closure라는 것을 알 수 있음.
이 someFunctionWithEscapingClosure 함수는 completionHandler 클로저를 실행시키지 않고 반환되고, 이 클로저는 아직 실행되지 않은 상태이다.
이때 compeltionHandlers 배열에 이 클로저를 집어넣는 거임.
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"
completionHandlers.first?()
print(instance.x)
// Prints "100"
self 도 명시를 해 줘야 한다. non-escaping 클로저는 암시적으로 그냥 사용하면 됐지만, escaping 클로저는 self를 써 줘야 함.
공식문서에 있는 예제는 해석하기 좀 어려워서 다음에 보고,
이 escaping closure가 쓰이는 이유를 알아보자.
Escaping closure는 A 함수가 마무리된 상태에서만 B 함수를 실행시킬 수 있다는 장점이 생김!
즉, 비동기 처리를 할 때 편리해지는데, 대표적인 사용처가 HTTP Request CompletionHandler라고 한다.
HTTP Request 를 할 때는 내가 Request를 서버에 전송하고, 서버에서 주는 Response를 받아야 하는데, 이 Response를 받아오는 함수들이 비동기로 작동해서 Request를 주자마자 반환되버림.
이 Response가 Request 결과를 기다리게 하려면? 바로 Escaping closure를 쓰는 것!!
Escaping Closure를 사용하면 함수의 실행 순서를 정할 수 있다!