[야우쓰] 1주차(6) 클로저 심화

hello_hidi·2022년 11월 14일
0

야우쓰

목록 보기
6/20
post-thumbnail

클로저 심화

@autoclosure

파라미터로 전달된 일반 구문 & 함수를 클로저로 래핑하는 것

즉 실제 클로저를 전달받지 않지만, 클로저처럼 사용이 가능해진다~!
다만 실제 클로저를 전달하는 것이 아니기 때문에 파라미터로 값을 넘기는 것처럼
()를 통해 구문을 넘겨줄 수가 있음!

func doSomething(closure: @autoclosure() -> () {
}

@escaping

non-escaping Closure

지금까지 우리가 봤던 건 모두 non-escaping closure였다
함수 내부에서 직접 실행하기 위해서만 사용한다.
따라서 i) 파라미터로 받은 클로저를 변수나 상수에 대입할 수 없고,
ii) 중첩 함수에서 클로저를 사용할 경우, 중첩함수를 리턴할 수 없다.

함수의 실행 흐름을 탈출하지 않아, 함수가 종료되기 전에 무조건 실행되어야 한다.

// i)
func doSomething(closure: () -> ()) {
	let f: () -> () = closure // error
}
// ii)
func outer(closure: () -> ()) -> () -> () {
	func inner() {
    	closer()
    }
    return inner // error
}

@escaping

즉 우리는 지금까지 클로저가 탈출하려면 바깥에 정의된 변수에 저장하는 방법 뿐이었다.
So, 함수 실행을 벗어나서 함수가 끝난 후에도 클로저를 실행하거나,
중첩함수에서 실행 후 중첩 함수를 리턴하고 싶거나, 변수 상수에 대입하고 싶은 경우!!! 사용!!!

func doSomething(closure: @escaping () -> ()) {
}

// 변수나 상수에 파라미터로 받은 클로저를 대입할 수 있다.
func doSomething(closure: @escaping () -> ()) {
	let f: () -> () = closure
}

BUT. escaping 클로저를 사용할 경우 주의해야할 점이 있는데 바로 메모리 관련!

만약 함수가 종료된 후 클로저를 실행하는데, 이때 클로저가 함수 내부 값을 사용하면 함수는 이미 종료 되었는데, 클로저는 함수 내부 값을 어떻게 사용할까? (뒤에서 봅시다)

Reference Capture

클로저는 값을 캡쳐할 때 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
    
    */
  • 먼저 closure은 외부 변수를 클로저 내부에서 사용하기 때문에 num 캡쳐
    이때 Referecne Capture로 num이란 변수를 참조
  • 따라서 closure을 실행하기 전 num이란 값을 외부에서 변경하면 클로저 내부에서 사용하는 num 또한 바뀐다!

클로저의 캡쳐 리스트

근데 난... 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)")
}

Reference Type의 값을 캡쳐 리스트로 하면?

Q. Reference 타입의 값도 Capture Lists에 작성하면, Value Capture가 될까?
A. 응 안돼~ 캡쳐 리스트를 작성한다고 해도,Reference Type은 Referce Capture을 한다.

Q. 그러면 Reference 타입은 Closer Capture List 필요 없겠네 ㅋㅋㅋ
A. 그건 또 아닌데.... (그럼 뭔데, 근데 이건 내가 아직 ARC를 몰라서 난중에 보자고!)

profile
안뇽희디

0개의 댓글