봉식이와 캔따개

왜 [self] 아니고 [weak self]로 써야할까 본문

Swift/TIL

왜 [self] 아니고 [weak self]로 써야할까

봉식이누나 2023. 9. 17. 15:41

 

 

 

어렴풋이 이해하고 넘어가서 자꾸 헷갈리는 부분이 생긴다 😢

클로저의 강한 순환 참조를 해결하는 법을 공부하다가 캡처 리스트와 weak가 급 헷갈리기 시작했다.

 

일단 closure의 capturing value부터 다시 살펴보자.

 

 

 

✅  capturing value

 

공식 문서

 

 

클로저는 자신이 정의된 주변 context로 부터 변수, 상수를 캡쳐 할 수 있다고 한다.

심지어 상수와 변수를 정의한 original scope가 더이상 존재하지 않더라도, 클로저는 그 값을 참조하거나 변경할 수 있다.

 

호엥...🫢

공식 문서의 예제를 참고해보자.

 

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

 

 

makeIncrementer 함수는 ( ) -> Int 형식인 incrementer 함수를 반환하고 있다.

incrementer 함수는 매개변수 값이 없고 Int를 반환한다.

 

일단 실행시켜보자.

 

 

엥? ㅋ

 

 

makeIncrementer 메서드가 한번 실행되고 나면 runningTotal은 메모리에서 해제되고 10이 세번 나와야할 것 같은데

값이 계속 추가되어 10, 20, 30 이런식으로 나오고 있다.

 

incrementer 내부에 runningTotal의 주소값을 출력하는 코드를 끼워넣고 실행시켜보았다.

 

 

 

runningTotal들이 같은 메모리 주소값을 출력해주고있다. 참조타입인줄?ㅎ

알고보니 클로저는 값타입이건 참조타입이건 reference capture를 한다고 한다.

 

 

 

 

다시 공식문서를 보면..

 

 

클래스 인스턴스의 프로퍼티에 클로저를 할당하고 

그 인스턴스나 인스턴스의 멤버를 참조하여 클로저가 해당 인스턴스를 캡쳐 하면

클로저와 인스턴스 사이에 강한 참조 사이클이 만들어진다.

 

Swift는 capture list를 사용하여 이 강한 참조 사이클을 끊는다.

 

드디어 capture list 개념이 나왔다.

 

 

 

✅  capture list

 

capure list를 사용하면 value captrue를 할 수 있다.

아주 간단한 예시를 만들어보자면...

 

 

var x = 0
var y = 0
let closure1 = {
print("closure1 x : \(x)")
}
let closure2 = { [x] in
print("closure2 x : \(x)")
}
x += 1
closure1()
closure2()
print("x : \(x)")

 

출력값은

closure1은 reference capture 했기 때문에 실행되는 시점에서의 x값, 즉 변경된 값 1로 출력되고

closure2는 value capture 했기 때문에 클로저가 생성되던 그 시점의 x값이 capture되어서 0이 출력된다.

 

 

오오 굿굿...

 

근데 이걸 값 타입이 아닌 참조타입에 적용해보면 어떻게 될까???

즉... [weak self] 말고 [self]로 써도 강한 참조 문제가 해결되는거 아닌가??? 🤔 → 아님

 

왜 아닌지 살펴봐야겠다.

 

 

 

다시 간단한 예시를 만들어보았다.

 

class Cat {
var name: String = "봉식"
}
let master = Cat()
let closure = { [master] in
print(master.name)
}
master.name = "태식"
closure()

 

출력값은??

 

 

쥬륵..... 봉식이가 태식이가 돼버렸음..

캡처 리스트를 사용했지만 똑같이 reference capture가 일어났다.

왜일까요...?

 

 

나는 이런 식으로 이해했다.

 

 

 

 

master는 참조타입이기 때문에 값 자체가 아니고

인스턴스의 실제 데이터가 위치한 heap 메모리의 주소값을 저장하고있다.

이걸 캡쳐 리스트를 사용해서 value capture를 한다고 하면??

 

 

 

 

 

주소값이 복사되고 이미 존재하는.. 인스턴스를 가르키게된다.

= reference count가 증가한다.

 

따라서 weak를 사용해서 reference count가 증가되는걸 막아줘야한다.

 

 

 

요렇게 이해했다ㅎ...

맨 처음 썼던 글인 struct vs class 가 떠오르는 부분이다..

 

반응형
Comments