Closure - @escaping을 중심으로

Young Bin Lee·2023년 6월 26일
1

research

목록 보기
1/6

클로저는 코드에서 전달하고 사용할 수 있는 독립된 function 블록입니다. Swift의 클로저는 C 및 Objective-C의 블록 및 다른 프로그래밍 언어의 람다와 유사합니다.

개요


클로저는 상수와 변수가 정의된 컨텍스트에서 해당 상수와 변수에 대한 참조를 캡처하고 저장할 수 있습니다. 이를 상수와 변수에 대한 클로저라고 합니다. Swift는 캡처의 모든 메모리 관리를 대신 처리합니다.

Closure vs Function

전역 함수와 중첩 함수는 사실 클로저의 특수한 경우이며 함수로서 클로저는 세 가지 형태 중 하나를 취할 수 있습니다:

  • 전역 함수는 이름이 있고 값을 캡처하지 않는 클로저입니다.
  • 중첩(nested) 함수는 이름이 있고 둘러싸는 함수의 값을 캡처할 수 있는 클로저입니다.
  • 클로저 표현식은 주변 컨텍스트에서 값을 캡처할 수 있는 약식 구문으로 작성된 이름 없는 클로저입니다.

@escaping


클로저를 함수에 대한 인수로 전달하고 함수 return 후에 호출되는 경우 클로저는 함수에서 이스케이프 된다고 표현합니다.

  • 클로저를 매개변수 중 하나로 사용하는 함수를 선언할 때 매개변수 유형 앞에 @escaping을 작성하여 클로저가 이스케이프할 수 있음을 나타냅니다.
  • 클로저가 이스케이프되는 방법 중 하나는 함수 외부에 선언한 변수에 저장할 경우입니다.
    • 예를 들어 비동기 연산을 처리하는 함수는 컴플리션 핸들러로 클로저를 사용합니다.
    • 함수는 연산을 시작한 후 결과를 표시하기 전 반환합니다. 이 때, 연산이 완료될 때까지 클로저를 호출하지 않으므로 클로저를 이스케이프해 나중에 호출할 수 있게 만들어야 합니다.
  • 대표적으로 다음과 같은 예시가 있습니다.
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

Retain behavior

self가 클래스의 인스턴스를 참조하는 경우 self를 참조하는 이스케이프 클로저는 특별한 주의가 필요합니다.

  • 이스케이프 클로저에서 self를 캡처하면 실수로 strong 리테인 사이클을 만들 수 있습니다.
    • 참조 주기에 대한 자세한 내용은 ARC를 참고하세요.
  • 일반적으로 클로저는 클로저 본문에서 변수를 사용하여 암시적으로 변수를 캡처하지만, 이스케이프 클로저는 이를 명시적으로 캡처해야 합니다.
  • self를 캡처하려면 사용할 때 self를 명시적으로 작성하거나 클로저의 캡처 목록에 self를 포함하세요. self를 명시적으로 작성하면 의도를 표현할 수 있고 참조 주기가 없는지 확인하도록 상기시켜 줍니다.

예시

func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"

completionHandlers.first?()
print(instance.x)
// Prints "100"
  • someFunctionWithEscapingClosure 함수에 전달된 클로저는 이스케이프 클로저로서 self를 명시적 참조합니다. **
  • 이와 대조적으로, someFunctionWithNonescapingClosure에 전달된 클로저는 non-escaping 클로저로서 암시적으로 self를 참조합니다.

non-escaping의 장점

@escaping으로 하지 왜?

@nonescaping은 Swift의 기본 동작으로 명시적으로 선언하지 않을 경우 인자로 전달되는 클로저는 자동적으로 @nonescaping으로 선언됩니다.

  • @nonescapingself를 캡처하지 않아도 됩니다. 따라서 리테인 사이클을 유발하지 않습니다.
  • 컴파일러 최적화(인라이닝)가 가능합니다.

참고

Documentation

0개의 댓글

Powered by GraphCDN, the GraphQL CDN