프로그래밍iOS 개발자

스위프트에서 escaping 및 non-escaping 클로저란 무엇인가요? 클로저 작업을 올바르게 구성하는 방법과 사용 시 주의할 점, 비동기 코드를 사용할 때 어떤 위험이 있을 수 있는지에 대해 설명해 주세요.

Hintsage AI 어시스턴트로 면접 통과

답변.

Escaping closure는 호출 함수의 범위를 "떠나는" 클로저입니다 (예: 비동기 실행을 위해 저장됨).

기본적으로 스위프트에서 함수는 non-escaping closure를 받습니다: 클로저는 함수 호출 내에서 실행됩니다.

Escaping을 명시적으로 지정하려면 @escaping 키워드를 사용합니다:

func asyncTask(completion: @escaping () -> Void) { DispatchQueue.main.async { completion() } }

주요 차이점:

  • Escaping 클로저는 나중에 저장되고 호출될 수 있습니다.
  • Escaping 클로저에서 self를 캡처하기 위해서는 메모리 누수(retain cycle)를 방지하기 위해 [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 체크를 수행하는 것입니다.