클로저는 코드에서 전달하고 사용할 수 있는 독립된 function 블록입니다. Swift의 클로저는 C 및 Objective-C의 블록 및 다른 프로그래밍 언어의 람다와 유사합니다.
클로저는 상수와 변수가 정의된 컨텍스트에서 해당 상수와 변수에 대한 참조를 캡처하고 저장할 수 있습니다. 이를 상수와 변수에 대한 클로저라고 합니다. Swift는 캡처의 모든 메모리 관리를 대신 처리합니다.
전역 함수와 중첩 함수는 사실 클로저의 특수한 경우이며 함수로서 클로저는 세 가지 형태 중 하나를 취할 수 있습니다:
@escaping
클로저를 함수에 대한 인수로 전달하고 함수 return
후에 호출되는 경우 클로저는 함수에서 이스케이프 된다고 표현합니다.
@escaping
을 작성하여 클로저가 이스케이프
할 수 있음을 나타냅니다.이스케이프
되는 방법 중 하나는 함수 외부에 선언한 변수에 저장할 경우입니다.이스케이프
해 나중에 호출할 수 있게 만들어야 합니다.var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
self
가 클래스의 인스턴스를 참조하는 경우 self
를 참조하는 이스케이프
클로저는 특별한 주의가 필요합니다.
이스케이프
클로저에서 self
를 캡처하면 실수로 strong 리테인 사이클을 만들 수 있습니다.이스케이프
클로저는 이를 명시적으로 캡처해야 합니다.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
를 참조합니다.다 @escaping
으로 하지 왜?
@nonescaping
은 Swift의 기본 동작으로 명시적으로 선언하지 않을 경우 인자로 전달되는 클로저는 자동적으로 @nonescaping
으로 선언됩니다.
@nonescaping
은 self
를 캡처하지 않아도 됩니다. 따라서 리테인 사이클을 유발하지 않습니다.