Escaping closure는 호출 함수의 범위를 "떠나는" 클로저입니다 (예: 비동기 실행을 위해 저장됨).
기본적으로 스위프트에서 함수는 non-escaping closure를 받습니다: 클로저는 함수 호출 내에서 실행됩니다.
Escaping을 명시적으로 지정하려면 @escaping 키워드를 사용합니다:
func asyncTask(completion: @escaping () -> Void) { DispatchQueue.main.async { completion() } }
주요 차이점:
[weak self] 또는 [unowned self]를 명시적으로 지정해야 합니다.DispatchQueue.async에 대한 매개변수로 전달되는 클로저에 항상 @escaping을 작성해야 하나요?
— 네. DispatchQueue.async가 클로저를 실행 시점까지 저장하기 때문에 클로저는 escaping이 됩니다.
예시:
func foo(action: () -> Void) { DispatchQueue.main.async { action() // 컴파일되지 않음: 클로저는 escaping이어야 합니다. } } // 필요: func bar(action: @escaping () -> Void) { ... }
이야기
컨트롤러가 escaping closure 내부에서 self에 대한 강한 참조를 만들었습니다 (예: 네트워크 요청). 컨트롤러는 화면에서 사라진 후에도 해제되지 않았습니다 — 강한 retain cycle. 해결책: [weak self] 또는 [unowned self] 사용.
이야기
함수가 클로저를 DispatchQueue.async에 전달했으나 escaping으로 표시하지 않았습니다. 프로젝트가 컴파일되지 않았고, 중첩 함수 때문에 오류를 찾기 어려웠습니다.
이야기
클로저 내부에서 클로저 호출 시점에 이미 비정의된 객체에 접근했습니다 ( [unowned self] 사용). 결과적으로 — 런타임 크래시. 해결책: [weak self] 사용 및 nil 체크를 수행하는 것입니다.