Swift의 escaping 클로저 이해하기
함수 실행이 끝난 뒤에도 호출되는 escaping 클로저의 생명주기와 메모리 관리 시 주의점을 살펴봅니다.

클로저가 함수 안에서 바로 실행되는지, 함수가 끝난 뒤에 실행되는지는 메모리와 캡처 방식에 영향을 준다. @escaping은 그 생명주기가 함수의 범위를 벗어난다는 사실을 호출부에 알린다.
Non-Escaping Closure
일반적으로 함수의 파라미터로 전달된 클로저는 함수 내에서만 실행되며, 함수의 범위를 벗어나면 자동으로 소멸한다.
class EscapingViewModel: ObservableObject {
@Published var text: String = "Hello"
func getData() {
downloadData { data in
text = data
}
}
func downloadData(completion: (_ data: String) -> Void) {
// 클로저 실행 후 함수 종료
completion("New Data!")
}
}
Escaping Closure
@escaping 속성이 지정된 클로저는 함수의 범위를 벗어나도 계속해서 실행될 수 있다. 주로 클로저가 비동기(asynchronous) 작업을 수행하는 경우, @escaping 속성을 사용하여 클로저가 함수의 범위를 벗어날 수 있도록 허용할 수 있다.
class EscapingViewModel: ObservableObject {
@Published var text: String = "Hello"
func getData() {
// 비동기 처리를 위한 약한 참조 처리
downloadData { [weak self] data in
self?.text = data
}
}
func downloadData(completion: @escaping (_ data: String) -> Void) {
// 함수 종료 후 2초 뒤 클로저 실행
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
completion("New Data!")
}
}
}
Model 생성 및 Typealias 활용
데이터 모델을 생성하여 여러 데이터에 더 쉽게 접근할 수 있다.
class EscapingViewModel: ObservableObject {
@Published var text: String = "Hello"
func getData() {
downloadData { [weak self] DownloadResult in
// 데이터 접근
self?.text = DownloadResult.data
}
}
func downloadData(completion: @escaping (DownloadResult) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
let result = DownloadResult(data: "New Data")
completion(result)
}
}
}
// 데이터 모델 생성
struct DownloadResult {
let data: String
}
typealias를 활용하여 코드를 더 간결하게 정리할 수 있다.
class EscapingViewModel: ObservableObject {
@Published var text: String = "Hello"
func getData() {
downloadData { [weak self] DownloadResult in
self?.text = DownloadResult.data
}
}
// 지정한 타입의 클로저 파라미터
func downloadData(completion: @escaping DownloadCompletion) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
let result = DownloadResult(data: "New Data")
completion(result)
}
}
}
struct DownloadResult {
let data: String
}
// 임의 타입명 지정
typealias DownloadCompletion = (DownloadResult) -> Void