[Swift] 탈출클로저(Escaping closure) 이해하기

dmsgk·2021년 6월 26일
0

iOS/Swift

목록 보기
3/3

주요 내용: 탈출클로저의 개념, 탈출클로저의 쓰임

아래와 같은 함수를 보면 completionHandler뒤에 붙어 있는 @escaping이 있습니다.

func dataTask(with url: URL, 
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask

이번 글에서는 @escaping이 의미하는 탈출클로저가 무엇인지, 어디에 쓰이면 좋을지 살펴보겠습니다.

1. 탈출클로저란?

탈출클로저는@escaping이 붙어있는 클로저로, 클로저가 함수로부터 '탈출'한다는 것은 해당 함수의 인자로 클로저가 전달되지만, 함수가 반환된 후 실행 되는 것을 의미합니다.

2. 탈출클로저를 왜 사용할까?

함수 내부의 클로저 인자를 외부에서 사용할 수 있습니다.

Swift3 이후부터는 기본적으로 함수의 인자로 들어오는 클로저는 함수 안에서만 사용할 수 있습니다. 즉 기본적으로 클로저를 함수 외부의 저장소에 저장하거나, GCD를 이용하여 다른 스레드에서 해당 클로저를 실행시킬 수 없습니다.

그러나 클로저 타입 앞에 @escaping 를 붙이면 탈출클로저로 바꿀 수 있고, 탈출클로저는 해당 함수가 반환된 후 실행되므로, 일반적인 클로저와 달리 함수 외부에 저장하거나 다른 스레드에서 실행시킬 수 있습니다.
그러나 탈출클로저를 이용해 함수를 만들면 함수의 실행이 끝난 후에도 메모리에 해당 부분이 남아있어 외부에서 활용하기 편합니다. ✌️✌️✌️✌️

만약 탈출클로저를 사용할 수 없다면 함수 내 클로저 인자를 함수 외부로 가져와서 가공할 수 없을 것입니다. 따라서 해당 값을 가공하려고 할 때마다 새로운 함수를 생성하여 클로저 인자를 만들고 새로 가공해야 하니 코드가 길고 복잡해질 것입니다.

+) 탈출클로저를 이용하면 깔끔한 아키텍쳐 설계에도 도움이 됩니다.

함수 간 실행 순서를 고려할 수 있습니다.

탈출클로저는 해당함수가 반환된 후 실행되는 클로저이므로, 함수 간 실행순서를 고려할 때 탈출클로저를 사용하여 코드를 짤 수 있습니다. 비동기로 여러 개의 작업이 처리될 때 함수의 동작 순서를 지정할 때 사용할 수 있습니다.

3. 탈출클로저 사용하기

3.1. 함수 밖에서 정의한 변수에 저장하기

함수 내 클로저 인자를 저장하기 위해서는 함수 밖에서 선언한 변수에 저장하면 됩니다. 아래 예시는 completionHandlers라는 변수에 someFunctionWithEscapingClosure라는 함수의 클로저인자인 completionHandler를 append하여 저장했습니다.

var completionHandlers = [() -> Void]()

func someFunctionWithEscapingClosure(completionHandler: @escaping() -> Void) {
	completionHandlers.append(completionHandler)
}

3.2.비동기 실행

GCD를 활용해 dispatch queue에서 비동기식으로 클로저를 실행할 때 큐는 클로저를 메모리에 보관하고 나중에 사용할 수 있습니다. 이 경우 클로저가 언제 실행되는지 알 수 없습니다. 아래 예시는 json이 담긴 url을 처리하여 DispatchQueue.global(qos: .background).async에서 처리하도록 하는 예시입니다.

func getJson(path: String, params: [String: Any], completed: @escaping (Result<String, Error>) -> Void) {
    DispatchQueue.global(qos: .background).async {
        do {
            let url = URL(string: path)
            let json = try String(contentsOf: url!, encoding: .utf8)
            DispatchQueue.main.async {
                completed(Result.success(json))
            }
            
        } catch {
            DispatchQueue.main.async {
                completed(Result.failure(error))
            }
        }
    }
}

탈출 클로저를 이해하는데 도움이 되셨기를 바라면서..이만 총총.

References

profile
배우는 것을 좋아합니다.

0개의 댓글