파라미터로 전달된 일반 구문 & 함수를 클로저로 래핑하는 것
즉 실제 클로저를 전달받지 않지만, 클로저처럼 사용이 가능해진다~!
다만 실제 클로저를 전달하는 것이 아니기 때문에 파라미터로 값을 넘기는 것처럼
()를 통해 구문을 넘겨줄 수가 있음!
func doSomething(closure: @autoclosure() -> () {
}
지금까지 우리가 봤던 건 모두 non-escaping closure였다
함수 내부에서 직접 실행하기 위해서만 사용한다.
따라서 i) 파라미터로 받은 클로저를 변수나 상수에 대입할 수 없고,
ii) 중첩 함수에서 클로저를 사용할 경우, 중첩함수를 리턴할 수 없다.
함수의 실행 흐름을 탈출하지 않아, 함수가 종료되기 전에 무조건 실행되어야 한다.
// i)
func doSomething(closure: () -> ()) {
let f: () -> () = closure // error
}
// ii)
func outer(closure: () -> ()) -> () -> () {
func inner() {
closer()
}
return inner // error
}
즉 우리는 지금까지 클로저가 탈출하려면 바깥에 정의된 변수에 저장하는 방법 뿐이었다.
So, 함수 실행을 벗어나서 함수가 끝난 후에도 클로저를 실행하거나,
중첩함수에서 실행 후 중첩 함수를 리턴하고 싶거나, 변수 상수에 대입하고 싶은 경우!!! 사용!!!
func doSomething(closure: @escaping () -> ()) {
}
// 변수나 상수에 파라미터로 받은 클로저를 대입할 수 있다.
func doSomething(closure: @escaping () -> ()) {
let f: () -> () = closure
}
BUT. escaping 클로저를 사용할 경우 주의해야할 점이 있는데 바로 메모리 관련!
만약 함수가 종료된 후 클로저를 실행하는데, 이때 클로저가 함수 내부 값을 사용하면 함수는 이미 종료 되었는데, 클로저는 함수 내부 값을 어떻게 사용할까? (뒤에서 봅시다)
클로저는 값을 캡쳐할 때 Value/Reference 타입에 관계 없이 Reference Capture을 한다.
아래 코드에서 num은 Int 타입의 구조체 형식이고, 이는 곧 Value 타입이기 때문에,
값을 복사해서 들고 저장해야 되는 것이 일반적임
But 클로저는 Value/ Reference 타입에 관계없이 캡쳐하는 값들을 참조함!
이것을 Reference Capture라고 한다.
func doSomething() {
var message = "Hi i am hidi!"
// 클로저 범위 시작
var num = 10
let closure = { print(num)}
// 클로저 범위 끝
print(message)
}
어떻게 되는거냐.. 한번 살펴보자!
func doSomething() {
var num: Int = 0
print("num check #1 = \(num)")
let closure = {
print("num check #3 = \(num)")
}
num = 20
print("num check #2 = \(num)")
closure()
/*
실행결과
num check #1 = 0
num check #2 = 20
num check #3 = 20
*/
근데 난... Value Type으로 Capture를 하고 싶은걸.....ㅠㅠ
이때 사용하는게 바로 캡쳐 리스트이다!
Value Type의 경우, Value Capture 하고 싶은 변수를 리스트로 명시!!!
- 클로저의 시작인 { 의 바로 옆에 []을 이용해 캡쳐할 멤버를 나열한다.
- 이때 in 키워드도 꼭 함께 작성한다.
ex) let closure = { [num, num2] in ...}
func doSomething() {
var num: Int = 0
print("num check #1 = \(num)")
let closure = { [num] in
print("num check #3 = \(num)")
}
num = 20
print("num check #2 = \(num)")
closure()
/*
실행결과
num check #1 = 0
num check #2 = 20
num check #3 = 0
*/
이때 주의해야할 점은!
Closure을 선언할 당시의 num의 값을 Const Value Type 즉 상수로 캡쳐
따라서 closure 내부에서 Value Capture 된 값을 변경할 수 없음!
let closure = { [num] in
num = 20 // error: Cannot assign to value (immutable capture)
print("num check #3 = \(num)")
}
Q. Reference 타입의 값도 Capture Lists에 작성하면, Value Capture가 될까?
A. 응 안돼~ 캡쳐 리스트를 작성한다고 해도,Reference Type은 Referce Capture을 한다.
Q. 그러면 Reference 타입은 Closer Capture List 필요 없겠네 ㅋㅋㅋ
A. 그건 또 아닌데.... (그럼 뭔데, 근데 이건 내가 아직 ARC를 몰라서 난중에 보자고!)