escaping closure 이해하기

권승용(Eric)·2024년 12월 3일

TIL

목록 보기
16/38

배경

  • escaping closure를 자주 사용하고 접하면서도, 정확히 어떤 상태일 때 escaping으로 동작하는지 잘 몰랐던 것 같아 정리하게 되었다.

escaping closure

  • 클로저가 함수의 매개변수로 전달되었지만, 함수 반환 이후(함수의 생명주기 외부에서)에 호출되는 경우 escape(탈출)한다고 할 수있다.
  • 이 때 인자 타입 이전에 @escaping을 작성해 해당 클로저는 탈출이 허용됨을 나타낼 수 있다.

상황 예시

  • 클로저가 탈출할 수 있는 하나의 예시는 함수 바깥에 정의된 변수에 저장되어 있는 경우이다.
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}
  • 위 예제는 클로저를 매개변수로 받아 함수 외부에 선언된 배열에 추가한다.
  • 추가된 클로저의 실행은 someFunctionWithEscapingClosure(_:) 함수가 종료된 이후에 일어나기 때문에, 해당 매개변수는 @escaping으로 표시되어야 한다.
    • 그렇지 않으면 컴파일 에러 발생!
  • 객체의 생성자 매개변수로 클로저를 전달받아 객체의 클로저 프로퍼티를 초기화할 때 @escaping을 통해 탈출 가능한 클로저임을 나타내야 하는 이유도 위와 같음.
    • 해당 클로저는 init 호출이 끝난 이후 호출될 것이기 때문!
  • 또 다른 예시는 익히 알고 있는 비동기적인 작업 이후의 컴플리션 핸들러.
    • 해당 핸들러의 호출이 원본 함수의 종료 이후에 이루어지기 때문에 @escaping으로 마크해줘야 함

self 캡처 주의

  • self가 클래스 인스턴스라면, 탈출 클로저 내부에서 self에 참조하는 것은 주의해야 한다.
  • 순환참조 발생 가능성이 있음.
  • escaping 클로저에서 self를 캡처하고 싶으면 명시적으로 self를 사용해야 함
    • self를 사용할 때 명시적으로 적어주거나, 캡처 리스트에 명시적으로 작성
  • 이를 통해 escaping 클로저 사용 중 순환 참조는 없는지 한 번 더 확인할 수 있음.
  • self가 구조체 또는 열거형의 인스턴스라면, 언제나 self에 암시적으로 참조 가능.
    • 그러나 탈출 클로저는 self가 구조체 또는 열거형이라면 self의 변경 가능한 참조를 캡처할 수 없다.
    • 구조체와 열거형은 공유 가능한 변경성을 허용하지 않기 때문.

정리

  • 함수의 파라미터로 클로저를 받았을 때, 해당 클로저가 함수의 생명주기 외부에서 호출 또는 실행된다면, 그 클로저는 escaping 클로저!
  • 탈출 클로저는 함수 흐름을 벗어나 실행될 수 있기 때문에, 클래스 인스턴스 self를 캡처할 경우 조심
  • 클로저가 클래스 인스턴스를 self로 참조하고, 클래스도 클로저를 참조하고 있다면 순환 참조 발생 가능
  • 따라서 캡처 리스트에서 weak 등의 키워드를 통해 관리 필요

출처

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/

profile
ios 개발자에용

0개의 댓글