안녕하세요! caution입니다. 오늘은 Escaping과 Non-Escaping에 대해서 살펴보겠습니다. Escaping 개념을 확립하기 위해서 Swift 공식 문서를 먼저 읽어보겠습니다!
Escaping Closures
클로저가 함수의 인수로 전달 되었지만 함수가 반환 된 후에 호출될 경우 클로저가 escape 되었다 고 부릅니다. 클로저를 매개 변수 중 하나로 사용하는 함수를 선언하면 매개 변수 유형 앞에 @escaping
을 사용하여 클로저가 escape 할 수 있음을 나타낼 수 있습니다.
클로저가 이스케이프 할 수 있는 한 가지 방법은 함수 외부에 정의 된 변수에 저장하는 것입니다. 예를 들어 비동기 작업을 시작하는 많은 함수는 completion handler로 closure 인수를 사용합니다. 이 함수가 작업을 시작한 후에 작업이 완료되어 반환될 때(return)까지 클로저(completion handler)가 호출되지 않습니다. 즉 클로저를 이스케이프 처리해야 나중에 호출 할 수 있습니다.
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
someFunctionWithEscapingClosure(_:)
함수는 인수로 클로저를 받아서 함수 외부에서 선언된 배열에 추가합니다. 이 때 이 클로저를 @escaping
으로 표시하지 않으면 컴파일 타임 오류가 발생합니다.
@escaping
으로 클로저를 표시하는 것은 클로저 내에서 명시적으로 자신을 참조해야 함을 의미합니다. 예를 들어, 아래 코드에서 someFunctionWithEscapingClosure(_:)
에 전달된 클로저는 이스케이프된 클로저이므로 self를 명시적으로 참조해야합니다. 반대로 someFunctionWithNonescapingClosure(_:)
에 전달 된 클로저는 이스케이프 처리되지 않았으므로 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"
정리
일반적인 로컬 변수들은 함수가 return 됨과 동시에 사라집니다. 이를 흔히들 함수와 같은 scope 를 가진다고 이야기 합니다. 하지만 인자로 넘어온 클로저가 함수의 scope와 다른 scope를 가져야 한다
면 이 클로저가 (함수의 scope를) escape 했다 라고 이야기합니다.
위의 예를 다시 살펴봅시다.
someFunctionWithEscapingClosure(_:)
: 인자로 받은 closure를 전역 변수인completionHandlers
에 넣습니다.someFunctionWithNonescapingClosure(_:)
: 인자로 받은 closure를 실행합니다.doSomething()
위 두 함수를 호출합니다.
someFunctionWithNonescapingClosure(_:)
는 인자로 받은 클로저를 실행하고 바로 return 하기 때문에 함수가 종료되면서 이 클로저 또한 사라지게 됩니다. 하지만 someFunctionWithEscapingClosure(_:)
는 전역변수에 클로저를 저장하기 때문에 doSomething()
함수가 return 되어도 클로저는 살아있어야 합니다. 즉 함수의 인자와 함수의 scope가 달라지기 때문에 이 경우 인자 앞에 @escaping
을 붙여주어야 합니다.