개발삽질기/iOS

@escaping 클로저란 뭘까?

세밍_ 2023. 6. 30. 15:48
728x90
반응형

@escaping은 클로저가 함수의 호출이 끝난 후에 호출될 것이라고 나타내는 키워드이다. 

클로저가 함수의 범위를 "탈출" 한다는 것을 나타낸다. 

@escaping은 클로저가 함수 호출이 끝난 후에 실행되거나, 백그라운드 작업으로 사용될때 유용하다. 

비동기 API 호출에서 completion handelr처럼 API 호출이 끝난 후에 실행되는 클로저에 주로 사용된다. 

 

스위프트에서 클로저를 매개변수로 가지는 함수를 작성할때, 함수의 실행이 끝난 후에도 그 클로저가 어딘가에 저장되어 나중에 호출되어야 한다면 해당 클로저 매개변수 앞에 @escaping 키워드를 붙여서 표기해야 한다. 

var completionhandler: (() -> Void)? = nil
func someFunction(completion: @escaping () -> Void) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
       completionhandler = completion()
    }
}

위 함수에서 클로저를 매개변수로 받아서 1초 후에 실행하게 된다.

순서를 자세히 봐보면 

1. 클로져가 함수의 completion 인자로 전달된다.

2. 클로져 completion이 completionhandler 변수에 저장된다. 

3. 함수가 Void 값을 반환하고(정확히는 반환 안함) 함수가 종료된다. 

4. 클로져 completion은 아직 실행되지 않는다. 

함수의 호출이 끝나도 클로저는 나중에 실행되므로 @escaping 키워드가 필요로 한다.

보통 클로저가 다른 변수에 지정되어서 사용하거나 비동기로 실행할때 사용된다. 

 

 escaping closure가 없는 경우도 있을까 

func runClosure(closure: () -> Void) {
 closure()
}

위 클로저가 실행되는 순서를 봤을때

1. 클로저가 runClousre()함수의 closure 인자로 전달이 되고

2. 함수 안에서 Clousure()가 실행된다. 

3. runClosure가 함수 값을 반환하고 종료된다. 

위 함수처럼 클로저가 함수가 종료되기 전에 실행되기 때문에 Closure는 Non-Escaping 클로저 이다. 

 

그런데 Non-Escaping 클로저를 @escaping이 붙어있는채로 사용할수도 있다.

( Escaping 클로저를 @escaping 없이는 사용할 수 없다. 꼭 @escaping이 필요하다)

 

그러면 모든 파라미터에 @escaping을 붙여버리면 되지 않나? 고 생각할 수도 있고, 더 나아가서 스위프트 자체에서 escaping으로 간주하면 안되나? 고 생각할수도 있다. 

 

하지만 이렇게 escaping과 non-escaping을 구분하는 이유는 컴파일러의 최적화때문에 그렇다. 

non-escaping 클로저는 컴파일러가 클로저의 실행이 언제 종료되는지 알기 때문에, 클로저에서 사용하는 특정 객체에 대해 retain, release 등의 처리를 생략해 객체의 라이프사이클을 효와적으로 관리할 수 있다. 

하지만 escaping 클로저는 함수 밖에서 실행되기 때문에, 클로저에서 사용하는 객체에 대한 추가적인 참조 사이클(reference cycles)를 관리해줘야 한다. 

이 부분이 컴파일러의 퍼포먼스와 최적화에 영향을 끼쳐서 스위프트에서는 필요할때만 escaping클로저를 사용하도록 해야 한다. 

 

<많이 참고함>

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

728x90
반응형