나는 스위프트 이전에 자바로 개발을 해왔기 때문에 .. 자바의 메모리 관리 방식을 생각해보면 가비지 컬렉션을 먼저 떠올릴 수 있다.
GC(Garbage Collection)
- 런타임 중 더 이상 필요가 없는 garbage라고 판단되면 메모리에서 해제
반면 스위프트는 ARC라는 메모리 관리 기법이 적용된다.
ARC(Automatic Reference Counting)
- 컴파일시 자동으로 retain, release 코드를 생성
- 참조 횟수를 세어 더 이상 참조되지 않는 인스턴스를 메모리에서 알아서 해제
두 기법의 가장 큰 공통점은 개발자가 따로 메모리 해제에 대해 관리할 필요가 없다는 점이며, 가장 큰 차이점은 메모리의 해제가 결정되는 시점이다.
GC는 런타임 중 객체를 계속 감시하기 때문에 추가적인 오버헤드가 발생하는 반면, ARC는 컴파일 타임에 모두 결정되어 메모리 해제에 대한 코드가 바이너리 코드에 추가되는 것이기 때문에 런타임에는 추가적인 오버헤드가 발생하지 않는다. 이러한 메모리 관리 기법의 차이는 프로그램이 구동되는 플랫폼에 따른 것이라는 생각이 들었다. 런타임 중 오버헤드로부터 비교적 여유로운 PC와 달리 모바일 플랫폼은 CPU와 메모리의 성능의 제한이 있기 때문에, 조건에 맞는 메모리 기법을 적용하지 않았을까? 하는 합리적인 추론..
그런데 개발자들로부터 'ARC의 순환참조를 조심해'라는 말을 심심치 않게 들어볼 수 있다.
순환참조
- 서로가 서로를 소유하고 있어 절대 메모리 해제가 되지 않는 것
- 따라서 memory leak이 발생
- 앱에서 Out of memory 크래시 발생할 것
근데 순환참조가 발생하면 안되는 이유가 단지 '메모리 누수'뿐인가?라는 궁금증이?
- 프로그램의 안정성과 성능 저하
- 결합도를 높여 유지보수성, 확장성, 재사용성 저하
그럼 순환참조를 방지하는 방법은 무엇이 있을까.. 마침 지피티가 답변에 덧붙여 알려주었다.
약한참조 weak
- 인스턴스에 대해 소유권을 가지지 않고, 주소값만을 가지고 있는 포인터
- 따라서 본인이 참조하는 인스턴스의 참조 횟수를 증가시키지 않음
- retain이 발생하지 않으므로 release도 발생하지 않음
- 메모리가 해제될 경우 자동으로 nil로 초기화
- 그러므로 weak 프로퍼티는 반드시 옵셔널
weak var zena = Zena()
미소유 참조 unowned
- 인스턴스에 대해 소유권을 가지지 않음
- 따라서 본인이 참조하는 인스턴스의 참조 횟수를 증가시키지 않음
- nil이 될 수 없음. 따라서 옵셔널로 선언될 수 없음
unowned var zena = Zena()
weak 옵셔널 타입 대신 사용하여 좀 더 간결한 코딩이 가능하다. 그런데도 참조 방식이 두 개로 구분되어 있는 이유와 이들의 차이는 무엇이게요
weak vs unowned
weak는 객체가 사라지면 nil로 바꾼다.
unowned는 객체가 사라지면 댕글링 포인터가 남는다. 이 댕글링 포인터를 참조하게 되면 크래시가 발생하므로, unowned는 사라지지 않을 것이 보장되는 객체에만 사용되어야 한다.
반면 강하게 참조할 수도 있다.
strong
- 인스턴스에 대해 소유권을 가짐
- 따라서 본인이 참조하는 인스턴스의 참조 횟수를 증가시킴
- 값이 초기화되는 시점에 retain, 참조가 종료되는 시점에 release
평소 weak나 unowned를 명시하지 않고 인스턴스를 선언하는 경우가 더 잦은데, 이게 strong인거다. default로 strong임ㅇㅇ
var zena = Zena()
언제 어떻게 무엇을 써야할까
strong 참조 횟수를 증가시켜 ARC로 인한 메모리 해제를 피하고, 객체를 안전하게 사용하고자 할 때
weak 순환 참조에 의해 메모리 누수를 방지하기 위해
unowned 객체의 라이프사이클이 명확하고, 개발자에 의해 제어의 가능 여부가 명확한 경우
왜 알아봤냐면, weak self 때문에 공부해 봤음
똑같은거 맞고 이스케이핑 클로저 안에서 self를 캡쳐할 때 사용한다. 이스케이핑 클로저 안에서 self에 접근하려면 이를 명시해야 하는데, 이때 self를 캡쳐하면서 순환참조가 발생하는 것을 막을 수 있다.
위의 경우, API를 받아오면 publisher가 이를 감지해 받아온 응답을 클로저 안에서 처리할 수 있게 던져준다. 이때 escaping closure 안에서 self.anniversaryDetail에 응답으로 받은 데이터를 할당해주고 있다. 이처럼 지연할당이 일어날 경우 사용하여 메모리 누수를 방지할 수 있는 것이다.
weak self를 사용하는 경우?
- escaping closure에서 지연 할당이 일어나는 경우
- 클로저가 객체를 lazy하게 메모리에서 해제할 경우
알아서 메모리 관리해주는 기특한 ARC 탐구 끗 -
References
[Swift] [weak self]는 언제 사용할까?
우리는 무의식적으로 [weak self]를 활용할 때가 매우 많습니다. 흔히 [weak self]를 활용하는 이유를 메모리 릭이라고 합니다. 그렇다면, 우리는 항상 [weak self]를 활용하면 될까요? [weak self]를 언제 사
longlivedrgn-miro.tistory.com
[Swift] 클로저에서의 weak self 에 대해 알아보자
클로저를 사용하면서 weak self를 사용해본 경험이 있거나, weak self를 사용하는 코드를 본 적이 있을 것이다. weak self 를 왜 사용해야 하고, 언제 사용해야 하는지에 대해 알아보자. 1. weak self를 왜
bongcando.tistory.com
[Swift]weak와 unowned 사용 방법
강력 순환 참조(Strong Reference Cycle)를 벗어나기 위해 약한 참조(weak reference)와 미소유 참조(unowned reference)를 사용합니다. 이전 Objective-C를 사용할 때는 강력 참조와 약한 참조를 통해서 참조 계수(re
minsone.github.io
Shall we always use [unowned self] inside closure in Swift
In WWDC 2014 session 403 Intermediate Swift and transcript, there was the following slide The speaker said in that case, if we don't use [unowned self] there, it will be a memory leak. Does it mea...
stackoverflow.com
weak self를 항상 써야할까? 라는 질문에 unowned self와 비교한 답변을 볼 수 있는 글!