study record

[Swift] 탈출 클로저(escaping closure)란? 본문

Swift/스위프트 정리

[Swift] 탈출 클로저(escaping closure)란?

asong 2022. 3. 9. 15:44

클로저란?

어떤 기능을 하는 코드를 하나의 블럭으로 모아놓은 것이다. 함수도 클로저의 한 형태이다.

클로저의 큰 특징은 클로저가 선언된 위치에서 어떤 상태를 캡쳐하고 참조를 얻을 수 있다는 것이다.

 

탈출 클로저 Escaping Closure

탈출 클로저란 @escaping 가 붙은 클로저로 함수가 반환된 후 밖에서 실행시키는 클로저이다. 이를 활용하여 기존에 있던 함수 범위 내부의 자원들을 활용해서 비동기적 작업을 할 수 있다. 함수의 매개변수로 클로저를 전달하고, 전달받은 클로저를 실행하여 함수를 탈출한다. 

 

Swift에서는 함수의 파라미터로 전달된 클로저는 기본적으로 "함수 내부 스코프 안에서만" 사용이 가능하다. 기본 클로저는 함수 내부를 탈출할 수 없다는 의미에서 "탈출 불가"속성을 가지고 있다. 따라서 파라미터로 전달받은 클로저는 내부에서 직접 호출만 가능하고, 외부 변수나 상수에 저장하는 것이 불가능하다.

또한 탈출 불가 클로저는 파라미터로 클로저가 넘어와서 해당 함수가 끝나서 리턴되기 전에 반드시 해당 클로저가 실행이 된다는 특징이 있다. 즉, 해당 함수가 끝나면 파라미터로 넘어온 클로저는 사용이 불가능하다는 것이다.

 

탈출 클로저를 만들기 위해서는 파라미터에 @escaping을 달면 파라미터로 넘어온 클로저를 밖으로 탈출시킬 수 있다. 이렇게 클로저를 탈출 가능하게 하면 해당 클로저를 외부 변수, 상수에 저장할 수 있다. 그리고 해당 함수가 끝나서 리턴이 된 후에도 클로저 실행이 가능하다. 탈출 가능 속성으로 정해두면 해당 클로저만을 위해 메모리 공간을 만들어서 함수가 종료되어도 클로저가 메모리 상에서 없어지지 않고 남아 있는 것이다.

 

 

언제 탈출 클로저를 쓰는가?

예시로 서버에서 이미지를 받아와서 UIImage를 반환하는 함수가 있다고 하자. 탈출 클로저를 사용하지 않으면 서버에서 이미지를 받아오기 전에 이미 이미지를 리턴할 것이다. Alamofire의 완료 여부와 상관없이 리턴이 실행되는 것이다.

// 데이터 전달
func getImage(from url: String, completion: @escaping (UIImage?) -> Void) {
    let task = URLSession.shared.dataTask(with: URL(string: url)!) { data, _, _ in
        guard let data = data, let image = UIImage(data: data) else {
            return
        }
        DispatchQueue.main.async {
            completion(image)
        }
    }
    task.resume()
}

// 데이터 받기(이미지 적용)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "MemberTableViewCell", for: indexPath) as! MemberTableViewCell
    
    getImage(from: imageURL) { image in
        cell.avatarImageView.image = image
    }
    return cell
}

 

따라서 탈출 클로저를 사용하여 함수가 이미 리턴이 되더라고 서버 작업이 완료될 때 이미지를 전달받을 수 있다.

즉, 탈출 클로저를 활용하면 기존에 있던 함수 스코프 내부의 자원들을 활용해서 (scope가 종료되더라도) 비동기적인 작업을 가능하게 해준다.

이처럼 서버 비동기 처리가 필요하거나 특정 함수가 종료될 때 특정 행동을 취해야 한다면 @escaping을 달아서 탈출 클로저를 활용하여 해결할 수 있다.

 

+변수로 클로저(completion handler) 선언해두고 사용하는 방식

*클로저는 기본적으로 non escaping이다. 옵셔널 타입 클로저는 escaping이 기본이다. (옵셔널 타입 클로저가 escape가 기본인 것이 아니라 non escape가 클로저의 기본이 되는 경우가 클로저 타입이 매개변수로 전달될 때인 것. 옵셔널 타입의 클로저는 클로저 타입이 아니라 제네릭 타입이기 때문인 것. )

 

참고 : 

https://i-colours-u.tistory.com/17

https://baechukim.tistory.com/38