Swift
3.0에 추가된 @noescape와 @escaping에 대해 살펴보자!
아래의
기사를 정리해서 번역한 내용입니다.
함수와
클로저는 Swift에서 일급 시민 객체라고 할 수 있다. 예를
들면 변수에 함수를 저장할 수 있고 함수에 파라메터로 넘길 수 있다. iOS의 많은 API에서 공통 패턴으로 completion handler에 클로저를
넘긴다.
Swift3에서는 함수에 클로저를 넘길 때 기본으로 non-escaping으로 지정된다. 다음과 같이 escaping과 non-escaping을 살펴보기로 한다.
Non-escaping
클로저의 라이프 사이클은 다음과
같다.
1.
함수에
클로저를 넘긴다.
2.
함수는
클로저를 실행한다.
3.
함수가
리턴된다.
클로저가
함수의 바디를 탈출하지 않는 것을 살펴본다. 함수가 종료되었을 때 패스된 클로저는 범위를 벗어나게되고
클로저에 추가 참조는 더이상 남아있지 않다.
메모리
관리에 대한 부분을 본자면 참조를 추가하고 해지하는 작업을 기억할 수 있다. 함수가 리턴된 후에 클로저
객체의 참조 카운트는 함수가 호출되기 전과 동일하게 된다.
Escaping
클로저
아마도
이제는 “escaping closure”가 무엇을 의미하는지 추측할 수 있을 것이다. 함수 안에서 여전히 클로저를 실행할 수 있다. 클로저는 함수 바깥쪽에
저장되어 실행된다. 아래와 같은 경우에 escaping 클로저가
실행된다.
1)비동기 실행(asynchronous
execution): 만약 클로저가 디스패치 큐에서 비동기적으로 실행되야 한다면 큐는 클로저를 잡고 있어야 한다. 함수가 리턴되기 전에 실행이 완료될 것이라고 확신할 수 있다면 이 부분이 필요하다.
2)저장소(Storage):
전역 변수나 프로퍼티에 클로저를 저장한다. 클로저가 탈출할 수 있도록 함수 호출을 전달해서
살아있도록 저장소에 저장한다.
함수가 종료되어도 클로저는 어딘가 바깥쪽에서 여전히
실행되어야 한다.
Swift1, 2에서는 클로저 파라메터는 기본적으로 escaping이었다. 만약에 클로저가 함수 바디를 탈출하지 않는다면
개발자가 @noescape속성을 파라메터 앞에 붙이면 되었다.
Swift3에서는 클로저의 파라메터가 기본이 non-escaping이 되었다. 만약 함수를 탈출한다면 파라메터 앞에
@escaping속성을 붙이면 된다. 만약 클로저가 non-escaping된다면 약간의 잠재적인 최적화가 된다. 클로저가
함수를 탈출하지 않기 때문에 컴파일러는 저장소와 클로저 호출을 최적화 할 수 있다.
class ClassA {
//클로져롤 사용하면서 기본으로 non-escaping
func someMethod(closure: ()
-> Void) {
}
}
class ClassB {
let classA = ClassA()
var someProperty = "Hello"
func testClosure() {
classA.someMethod {
//self가 캡쳐된다.
someProperty = "Inside the
closure!"
}
}
}
클로저 안에서 someMethod를
호출할 때 someProperty는 ClassB의 속성이다. 기존에는 클로저 안에서 늘 self를 사용해야 하는 요구사항이 있었지만
현재는 기본값이 non-escaping이기 때문에 Swift3에서는
문제가 없다. 컴파일러는 순환 참조 리스크가 없다는 것을 알고 있고 클로저 파라메터는 없어지게 된다.
반면에 만약 메서드 선언에 다음과 같이 추가하면 메서드는
클로저와 함께 무엇을 할지를 알게된다. 어딘가 저장하거나 큐에 저장한다. 개발자가 이 메서드를 호출 할 때 클로저는 프로퍼티를 참조한다. 개발자는
반드시 클로저 바디에 self를 명시해야 하며 캡쳐되는 것을 리마인드 할 수 있다.
class ClassA {
//클로져롤 사용하면서 기본으로 non-escaping
//아래 코드에 @escaping을 추가
func someMethod(closure: @escaping () -> Void) {
}
}
class ClassB {
let classA = ClassA()
var someProperty = "Hello"
func testClosure() {
classA.someMethod {
//self가 캡쳐된다.
//다음과 같이 self를 명시해야 한다.
self.someProperty = "Inside the
closure!"
}
}
}
댓글 없음:
댓글 쓰기
참고: 블로그의 회원만 댓글을 작성할 수 있습니다.