[Swift] @escaping

5628 단어 swiftswift

@escaping

  • Escaping 클로저는 클로저가 함수의 인자로 전달됐을 때, 함수의 실행이 종료된 후 실행되는 클로저.
  • Non-Escaping 클로저는 이와 반대로 함수의 실행이 종료되기 전에, 즉 함수 내부에서 실행되는 클로저.

Non-Escaping Closure

func runClosure(closure: () -> Void) {
    closure()
}
  • 이런 식으로 선언되어 있으면
    1. runClosure에 클로저가 전달되고
    2. 전달된 클로저인 closure()가 실행되고
    3. runClosure가 종료된다

Escaping Closure

  • 주로 네트워킹에서 completion handler를 다루는데 사용한다
class ViewModel {
    var completionhandler: (() -> Void)? = nil
    
    func fetchData(completion: @escaping () -> Void) {
        completionhandler = completion
    }
}
  1. fetchData에 completion 클로저가 전달되고
  2. completion 클로저가 completionhandler에 저장된다
  3. fetchData는 값을 반환하고 종료된다
  4. 클로저 completion은 아직 실행하지 않는다

그러나 @escaping 가 붙은 클로저에는 반드시 escaping 클로저만 인자로 사용할 수 있는 것은 아니라고 한다.

Non- Escaping Closure + @escaping

func runClosure(closure: @escaping () -> Void) {
   closure()  // ✅ closure는 non-escaping 클로저이지만 @escaping 사용 가능
}

하지만 반대로 @escaping 가 붙지 않은 클로저 인자에는 escaping 클로저를 넘길 수 없다.

Escaping Closure + Not @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 클로저를 사용하도록 구분해 두었습니다.

출처: https://jusung.github.io/Escaping-Closure/

좋은 웹페이지 즐겨찾기