오늘은 메모리 관점에서 클로저에 대해 더 이전보다 조금 더 알아봤다.
공부한 순서와 키워드를 기록하자면,
Closure(Closure,Trailing Closure,Escaping Closure) -> 메모리 기본(메모리 공간, stack과 heap) -> Reference TypeValue Type -> ARC(Reference Count, Strong Reference, Weak Reference, Unowned Reference) -> Strong Reference Cycle -> Capture List

아직 다른 부분은 추가적인 학습이 필요한 것 같아서 Closure에 Capture에 대해서만 기록해보려고 한다.
이 내용은 야곰이 오픈한 홈페이지에도 올렸다! (https://yagom.net/forums/topic/closure의-capture/)

Closure의 Capture

1. 클로저는

func makelncrementer(forlncrement amount: Int) -) (() -) Int) { 
var runningTotal =0
func incrementer() -> Int {
runningTotal += amount return runningTotal
  }
return incrementer
}

let incrementByTwo: (() ->Int) =makelncrementer(forlncrement: 2) 
let sameWithlncrementByTwo: (() -> Int ) = incrementByTwo
let first: Int =incrementByTwo() // 2 
let second: Int =sameWithlncrementByTwo() // 4

클로저는 자신이 정의된 위치의 주변 문맥을 통해 상수나 변수를 획득(capture)할 수 있습니다. 값 획득(Capture)을 통해 클로저는 주변에 정의한 상수나 변수가 더 이상 존재하지 않더라도 해당 상수나 변수의 값을 자신 내부에서 참조하거나 수정할 수 있습니다.

상수에 클로저를 할당한다는 것은 클로저의 값을 할당하는 것이 아니라 해당 클로저의 참조를 할당하는 것입니다.

2. 클로저는 클래스와 마찬가지로 Reference Type

값 전달할 때 마다 stack에 주소가 복사됩니다.
참조 방식에서 '참조'는 stack에 저장되어있는 주소를 의미합니다.

3. 클로저의 캡쳐와 캡쳐리스트를 통한 캡쳐

var firstOne = 0
var secondOne = 0
let closure = {print (firstOne,secondOne)}
firstOne = 1
secondOne = 2
closure() // 결과 : 1,2

closure 상수에 저장된 클로저는 firstOne, secondOne 두 상수를 캡쳐합니다.
클로저가 값을 캡쳐할 땐 복사본이 아니라 참조(값의 주소, reference)가 전달합니다. 두 변수를 변경하고 클로저를 호출하면 클로저가 생성된 시점에 두 변수에 저장되어있던 값이 아니라 변경된 현재 값이 출력됩니다.

var firstOne = 0
var secondOne = 0
let closure = { [firstOne] in print (firstOne,secondOne)}
firstOne = 1
secondOne = 2
closure() // 결과 : 0,2

하지만 캡쳐 리스트(Capture List)를 통해 firstOne을 캡쳐하면 이전과 달리 참조가 전달되지 않습니다. 왜냐하면 firstOne에 저장되어있는 값과 동일한 값을 가진 상수 복사본이 전달되기 때문입니다. 그래서 클로저를 선언한 후(3번째 라인)에 fisrtOne 값을 변경하더라도, 변경되지 않은 클로저에서 캡쳐된 firstOne의 값이 출력됩니다. 즉, 캡쳐 대상으로 지정한 firstOne은 값이 복사되서 전달됐으므로 0이 출력됩니다.

4. 클로저 내 self

class Person{
var name = "Lena"
var job = "developer"
lazy var nameTag: () -> String = {
return self.name + self.job
  }
}

클로저는 self 키워드를 통해 인스턴스 속성에 접근합니다. self는 인스턴스 자체를 나타내는 속성으로, 클로저에서 사용하면 self가 캡쳐됩니다. 위 예제 코드(3번)에서는 self가 Person이라는 클래스이고 이 클래스 인스턴스를 캡쳐합니다. 그리고 이 클래스의 속성인 namer과 job에 접근합니다. 클로저는 자신의 실행이 종료될 때 까지 self가 메모리에 유지되도록 강한참조로 캡쳐합니다.이 클로저는 인스턴스 속성(Person이라는 클래스의 프로퍼티)에 저장됩니다.
(3번 예제 코드는 클로저의 강한참조 순환문제가 일어나는 코드입니다. 이 문제는 Capture List로 해결할 수 있습니다.)

참고한 자료

'스위프트 프로그래밍 3판' (지은이: 야곰)
'Closures — The Swift Programming Language (Swift 5.2)'
https://docs.swift.org/swift-book/LanguageGuide/Closures.html

오늘 하루 소감
쉬는 동안 해야할 일을 적어봤는데 주어진 시간에 비해서 해야할 목록이 빠듯했다.
우선순위가 필요한 것 같다. 적어보지 않고 생각만했을 때는 쉬면서 충분히 할 수있을거라고 생각했는데, 적어보니 빠듯한 정도가 아니라 모자를 것 같다. 꼭 달성할 목표를 정해놓고 집중해야할 것 같다!
그리고 야곰 홈페이지에 글을 처음 써봤다. 내가 이해한 내용이 맞는지 올려보고 교정도 받을 수 있어서 좋은 기회인것 같다! 하지만 잘 모르는 상태에서 글을 쓰는 것 같아서 조금 부담스럽기는 하다. 하지만 표현을 해야 내가 아는 것과 모르는 것을 명확하게 알게 되니까 열심히하자!

profile
Swift, iOS 앱 개발 공부하고 있어요!

0개의 댓글