swift, escape, noescape, closure agument
시작하기 전에...
이 글은 escape 와 noescape 를 설명하는 글은 아니다.
func execute(handler: @escaping (() -> Void)?) {
}
Swift 4 에서 위와 같이 작성하게 되면 에러가 발생한다.
@escaping attribute only applies to function types
플레이 그라운드 콘솔 에러는 다음과 같다.
closure is already escaping in optional type argument
이에 관해 Swift 개발자들이 논의했던 내용과 소스 변경내역들을 추적해 봤다.
이전에는 클로저 타입 매개변수의 기본이 escape 였다.
먼저 왜 noescape 로 변경되었을까? 부터 찾아봤다.
클로저 타입 매개변수의 기본이 noescape가 된 이유는 다음과 같다.
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160530/019880.html
by 크리스 래트너
- Most functional algorithms written in pure Swift will benefit because they are naturally noescape. The core team feels that this will reduce the boilerplate involved with writing these algorithms.
- The compiler has enough logic in it to provide a great QoI experience when a developer doesn’t think about escaping, and tries to escape a closure - it can provide a fixit that suggests adding @escaping.
- Recent changes (to disallow escaping closures to close over an inout parameter) are pushing the language to prefer noescape closures. noescape closures have also always been the preferred default, since they eliminate a class of retain cycle issues.
- "@autoclosure(escaping)" can be simplified and standardized to "@autoclosure @escaping”
John McCall pointed out that resilience in the type system is different than resilience in practice: An API changing to capture a closure and use it long after it was originally passed is likely to break the clients regardless of whether the type system captures this as an issue. He argues (and the argument is strong IMO) that it is *better* for resilient APIs to default to @noescape, since that forces the author of V2 to think about whether they are breaking their clients. If they are doing something that is “logically” noescape in their V2, then they can unsafe bitcast away the escaping aspect of the closure. This is better than changing the client’s view of the API in any case.
*번역과 사족을 남겼었지만 어설픈 번역보다는 원문을 읽는게 좋은것 같아 삭제했다.
Swift 핵심팀은 위와같은 이유들로
클로저 타입의 매개변수의 기본을 noescape 로 바꾸게 된것이다.
아직 위의 "옵셔널 타입의 클로저 매개변수는 왜 escaping 이 기본인가?" 의문은 남아있다.
이와 관련된 이슈를 2개 찾을수 있었다.
https://bugs.swift.org/browse/SR-2324
두번째 링크의 중간에 글을 보면 아래와 같은 내용이 있다.
Swift 3에서 이스케이핑 처리에 대한 이슈
- 함수 인자로써의 클로저는 escaping 되지 않습니다.
- 다른 모든 클로저는 escaping 됩니다.
이건 모든 제네릭 타입의 클로저 인자 ( array 와 optional )은 이스케이핑 되는 걸 의미합니다.
함수 매개변수로 클로저가 전달될때 noescape 가 default 가 되도록 수정한 이유와 이력은 위에 정리된 것과 같음. 함수 매개변수로 클로저가 전달될때에만 default 가 noescape 가 되는 걸 의미함. 이외의 클로저는 escaping 이라는 것.
다시 한번 정리하면 옵셔널 타입 클로저 매개변수가 escape가 기본인것이 아니다.
noescape 가 클로저의 default 가 되는 경우는 클로저 타입이 매개변수로 전달될때 인 것.
옵셔널 타입의 (클로저) 매개변수는 제네릭 타입이다.
클로저 매개변수 타입이 아니다.
이제 첫문단에서 봤던 에러를 다시 보자.
func execute(handler: @escaping (() -> Void)?) {
}
Swift 4 에서 위와 같이 작성하게 되면 에러가 발생한다.
@escaping attribute only applies to function types
플레이 그라운드 콘솔 에러는 다음과 같다.
closure is already escaping in optional type argument
이제 에러가 한눈에 들어오며 '아~!' 하면서 이해가 되지않는가? :D
호기심으로 출발해 진행한 위와 같은 추적은 사실 시간만 투자하면 할수 있는 것이다.
그래도 이 과정에서 얻을수 있었던 것은, Swift 를 개발한 사람들이 어떤 철학을 가지고 개발을 진행햇고, 서로 의견교환은 어떤 식으로 했는지에 대해 약간이라도 엿볼수 있었던 것들이 색다른 느낌을 주었던것 같다.
Swift 의 여러 요소들이 이런 많은 고민과 토론속에서 결정되었을 것을 생각하며,
개발자가 코드를 작성할때 충분히 많은 고민을 해야 하는는 것과, 동료 개발자와 소통하는 과정들이 중요하다는 점에 대해 되돌아보고 앞으로의 다짐을 할수 있는 계기가 되었다.
마침.
요런 에러를 발견했다면 위의 글을 확인해보자
Attribute can only be applied to types, not declarations