func runClosure(closure: () -> Void) {
closure()
}
class ViewModel {
var completionhandler: (() -> Void)? = nil
func fetchData(completion: @escaping () -> Void) {
completionhandler = completion
}
}
그러나 @escaping 가 붙은 클로저에는 반드시 escaping 클로저만 인자로 사용할 수 있는 것은 아니라고 한다.
func runClosure(closure: @escaping () -> Void) {
closure() // ✅ closure는 non-escaping 클로저이지만 @escaping 사용 가능
}
하지만 반대로 @escaping 가 붙지 않은 클로저 인자에는 escaping 클로저를 넘길 수 없다.
class ViewModel {
var completionhandler: (() -> Void)? = nil
func fetchData(completion: () -> Void) { // ❗️@escaping 누락으로 컴파일 에러 발생!
completionhandler = completion
}
}
그러면 그냥 함수 선언부에 클로저 인자에 @escaping을 전부 붙이면 되지 않냐고 생각할 수 있다. 하지만 컴파일러의 퍼포먼스와 최적화를 위해 이렇게 나누어 사용한다고 한다.
아래는 출처에서 인용
non-escaping 클로저는 컴파일러가 클로저의 실행이 언제 종료되는지 알기 때문에, 때에 따라 클로저에서 사용하는 특정 객체에 대한 retain, release 등의 처리를 생략해 객체의 라이프싸이클(life-cycle)을 효율적으로 관리할 수 있습니다.
반면 esacping 클로저는 함수 밖에서 실행되기 때문에 클로저가 함수 밖에서도 적절히 실행되는 것을 보장하기 위해, 클로저에서 사용하는 객체에 대한 추가적인 참조싸이클(reference cycles) 관리 등을 해줘야 합니다. 이 부분이 컴파일러의 퍼포먼스와 최적화에 영향을 끼치기 때문에 Swift에서는 필요할 때만 escaping 클로저를 사용하도록 구분해 두었습니다.