✳️ 발단
[Swift] Escaping Closure(탈출 클로저)
@escaping
을 붙이지 않으면 스코프 밖의 변수인 클로저에 파라미터로 전달받은 클로저를 할당 불가능하다고 알고 있었는데 아래와 같은 코드가 잘 기능하는 것을 발견했다. @escaping을 붙이지 않았는데 어떻게 할당이 되었을까요? (탈출 클로저에 대한 설명은 위 티스토리 참조 ^__^)
private var leftButtonClosure: (() -> Void)?
@discardableResult
func leftButonAction(_ clousure: (() -> Void)? = nil) -> Self {
self.leftButtonClosure = clousure
self.leftButton.addTarget(self, action: #selector(leftButtonSelector), for: .touchUpInside)
return self
}
// shout out to Gyuios!
여러 번의 시행착오을 통해 클로저 매개변수를 optional로 선언하면 탈출 가능해진다는 사실을 알게 되었고!
이것이 가능한 이유를 알기 위해 googling의 세계로 떠났습니다...
✳️ History 분석
1. @escaping 키워드는 언제부터 생겼나?
https://brunch.co.kr/@tilltue/52
위 링크에는, swift 3으로 업데이트되면서 메모리 안정성을 위하여 클로저 매개변수는 기본적으로 함수 스코프 내에서 실행되도록 escape에서 non-escape으로 바뀌었고 필요한 경우에만 @escaping을 통해서 탈출시킬 수 있도록 하는 기능을 제공했다는 내용이 담겨 있습니다!
2. @escaping에 대한 다면적인 실험
위 링크를 보시면 오직 함수 타입에만@escaping
을 붙일 수 있다고 나옵니다. Xcode에서도 클로저 타입이 아닌 자료형에 @escaping
을 붙이면 실제로 에러가 뜨고 있어요~!
이러한 에러가 뜨는 [클로저 타입이 아닌 자료형]에는, optional로 선언한 클로저도 포함이 되어 있었습니다.
optional 클로저에 @escaping을 붙일 수 없다 ⇒ optional 클로저는 클로저 타입이 아니라 옵셔널 타입이다 ⇒ 옵셔널 타입은 클로저 타입이 아니기에 non-escape 원칙을 적용받지 않는다 ⇒ optional 클로저는 다른 타입 인자와 같이 외부로 탈출이 가능하다
✳️ 정리
- 스위프트에서 안정성을 위해서 함수 파라미터로 전달되는 클로저들은 기본적으로 탈출 불가하게 만들어 놓음(이유는 ARC로 인한 참조 문제 해결을 위해)
- 그래도 탈출시키고 싶은 클로저가 있으면 @escaping을 통해서 탈출할 수 있는 기능을 만들어줌
- 그런데 이러한 @escaping의 정의를 보면, 클로저 형식 앞에만 붙일 수 있다고 함
- 옵셔널 클로저에 @escaping을 붙여 보니, 클로저 형식이 아니라는 에러가 뜸
- 클로저를 옵셔널로 선언하면 클로저 타입이 아니라 옵셔널 타입이 됨
- 옵셔널 클로저는 클로저 타입이 아니라 옵셔널 타입이기 때문에 다른 타입과 같이 탈출도 가능하게 됨
📚 마무리 : 옵셔널 타입 클로저는 클로저 형식이 아니라 옵셔널 형식으로 취급받기 때문에, 1번에서 적은 클로저의 non-escape 규칙을 적용받지 않는다. 따라서 탈출 가능하다.