- Total
꿈꾸는리버리
Closure에서의 Capture ( feat . reference type) [2/3] 본문
저번 블로그 포스팅을 통해서 Closure가 무엇인지에 대해 알아봤다.
이번에는 Closure랑 좀 더 친해지는 ... 과정을 겪으려고 한다. ( 근데 아직 너무 어려움.... ^^ )
Closure의 Capture가 무엇인지, Closure가 reference type인지 몰랐다면..!
같이 알아보자구용 ~~ 🥹🔥✨🧚🏻♀️
Capturing Values
Capturing Value를 살펴보기 위해 ... "the swift programming language"에서 한 예시를 가지고 왔다 !
이 makeIncrementer 함수를 호출하면, 클로저가 반환된다.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
(참고
: 위 code은 중첩함수로 나타내어져 있는데,
중첩함수와 unnamedClosure 모두 value를 capture 하기 때문에
지금 설명하고자 하는 Capture에 대해 설명하는데에는 문제가 없지만,
나 같은 궁그미들을 위해 unnamedClosure 식도 밑에 추가로 두겠슘당.. ㅎㅎ)
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
let incrementer = {() -> Int in
runningTotal += amount
return runningTotal
}
return incrementer
}
1) 중첩 함수에 대한 이해 (with capture)
makeIncrementer 함수는 드래그한 부분 즉 , ()->Int인 closure을 반환하는 함수이다.
makeIncrementer내부에 있는 incrementer함수를 한번 떼어서 보자.
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
?? runningTotal ?? amount ?? 어디있어 ??
incrementer함수만 떼어서 보면 runningTotal이 무엇인지, amount가 뭔지 전혀 알 수 없다.
하지만 ,.,. 이렇게 함수를 실행하면 오류도 안뜨고 10이라는 결과도 잘 나온다...!
?? 이유가 뭐지 ??
이게 가능한 이유는 makeIncrementer() 중첩 함수의 scope에 있는
runningTotal과 amount을 capturing 했기 때문이다.
ㅎㅎ... 더 자세히 설명을 하면 !!
makeIncrementer 함수가 실행될 때 변수 runningTotal과 amount가 캡쳐링 되어
incrementer 함수에 두 주솟값이 공유가 된다.
이 때문에 함수 바깥에 있는 변수나 상수들도 함수 내에서 사용이 가능하다.
이때, runningTotal과 amount 모두 value type이지만 capturing될 때에는 reference type으로 캡쳐된다는 특징을 기억하자 !
reference type인 Closure
incrementByTen에 makeIncrementer()를 대입해보자 !
let incrementByTen : () -> Int = makeIncrementer(forIncrement: 10)
나는 closure을 변수에 넣는다는 것 조차 되게 낯설었는데,,
incrementByTen에는 Int, Double과 같은 하나의 값이 아니라
makeIncrementer의 반환값이었던 incrementer( ) 클로져 즉, ( )->Int 가 들어가 있다고 생각하면 연상하기 쉬울 것이다..
(아래 그림 드래그 한 부분이 들어가 있은 거다!)
자 이제 실행을 해보자 !
ㅎㅎ 뭔지 잘 모르겠었다... 그냥 잘 모르겠었다.... 나는 내가 뭘 모르는 지도... 모르겠었다.
계속 뚫어져라 보고 있으면 2가지 의문점이 드는데,, 가려운 부분을 긁어보자 ㅎㅎ!!
의문점 1) 왜.. 값이 10 -> 20 -> 30 누적되어 증가하는 거지 ???
incrementByTen를 호출할 때마다 값이 계속 증가하는데,,, 왜 증가할까?? ㅋㅋㅋㅋㅋㅋㅋㅋㅋ
그 이유는 Capturing Value 때문이다 !
앞서 말했던 거처럼 함수가 실행될 때 변수 runningTotal과 amount가 캡쳐링 되어 변수가 공유되기 때문에 계산이 누적되기 때문이다.
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
여기서 runningTotal과 amount의 값이 value 타입으로 저장되어 있는 게 아니라 reference type으로 저장되어 있기 때문에
저장되어 있는 주솟값에 접근하여 값을 증가시키기 때문에 값이 누적되어 계산이 이루어진다.
+ 당연한 말이지만, 새로 함수를 호출하면 또 캡쳐링을 하기 때문에 incrementByTen()와 incrementByTen2()는 다른 Int의 값이 나온다 .
의문점 2) incrementByTen은 Int형 상수인데 왜 값이 변하는 거지 ?
공문 내용을 읽어보면 , 변수나 상수에 함수나 클로져를 넣으면 reference type으로 저장이 된다.!
따라서 incrementByTen가 let이라도 계속 값이 바뀔 수 있었던 거고,
이렇게 변수에 넣으면 그 값이 저장되는 게 아니라 주솟값이 저장되기 때문에 이전의 값에서 이어 증가된 40이 출력되게 된다.
오늘 포스팅한 내용을 정리해보자면, 이렇게 작성할 수 있을 거 같다 :)
특징 | |||
Closure | named closure ( function ) | Global function | capture(x) |
Nested function | capture(o) | ||
unnamed closure | closure expression | capture(o), 경량식 |
+ swift 문서에 강한 순환 참조가 적혀 있는데 지금 내 머리로는 이해를 할 수 없어서 ㅠㅠ
이 부분은 다음에... 포스팅을 해보려고 한다.
+ reference type 부분은 나중에 메모리 공부를 좀 더 해서 그림을 통해 좀 더 디테일한 설명을 하려 한다... !
'오뚝이 개발자 > swift' 카테고리의 다른 글
removeLast() vs popLast() (0) | 2022.05.24 |
---|---|
[2/2] optional chaining (6) | 2022.05.14 |
[1/2] Optional unwrapping (0) | 2022.05.14 |
Closure 넌 누구냐 ! [1/3] (0) | 2022.05.11 |
Property(1/4) - > StoredProperty (0) | 2022.04.27 |