참조로 저장되는 객체들은 동시에 여러 변수에 의해 참조될 수 있다. 변수가 지운다고해도 해당 참조 객체를 참조하는 다른 변수가 있다면 객체를 메모리에서 지울 수 없다.
이러한 상황은 앱에서 더 이상 필요없는 객체들로 가득 차서 메모리를 낭비하는 상황을 만들어낸다.
Apple이 제공하는 솔루션은 객체를 참조하는 변수의 수를 계산하고 모든 참조가 지워질 때만 메모리에서 객체를 제거하는 자동 시스템입니다(모든 변수가 지워지거나, 설정되거나, 다른 객체에 대한 참조가 할당되었습니다). 그 시스템은 ARC(Automatic Reference Counting)라고 불린다. ARC는 메모리에 해당 공간에 대한 참조를 포함하는 상수나 변수가 더 이상 없을 때 개체를 자동으로 지웁니다.
다음에 정의된 클래스들을 보면 employee와 department라는 클래스를 참조하는 객체를 만들어냈다. employee?.location과 department?.person은 서로 다른 클래스인 객체를 참조하고 있다
만약에 위에서 만든 객체가 필요없다고해도 ARC(Automatic Reference Counting)는 해당 객체를 메모리에서 지우지 않는다.
swift는 이런 문제를 해결하기위해서 strong, weak, unowned가 있다, 기본 참조는 strong에 해당한다, 참조하는 객체가 있는 한 메모리에서 지우지 않는다. 이런 strong reference cylcle이라고 부른다. 이 cycle을 부수기 위해서는 strong 대신 weak 또는 unowned 로 만드는 것이다. ARC(Automatic Reference Counting)가 weak 또는 unowned를 만나게 됐을때 만약 참조하는 객체가 사라졌을때 employee 객체는 메모리에서 사라지게 된다.
Strong, Weak, unowned 차이점
원래는 객체에 nil을 넣어주어야 메모리가 해제되는 것을 확인할 수 있다. 하지만 Strong 같은 경우는 상호 참조하는 경우가 있을 경우 reference count가 0이 되지 못하여 메모리 누수(Memory Leak)가 발생하게 된다.
하지만 weak는 reference count는 변화가 없기 때문에 약한 참조로 참조하고 있는 동안에 메모리가 해제되는 것이 가능하다.
weak 를 사용하는 객체는 항상 optional 타입이여야 한다(해당 객체가 nil 일 수 있기 때문에)
unowned도 weak와 마찬가지로 객체를 참조하되 reference count는 변화가 없다. unowned 참조도 weak 참조와 마찬가지로 인스턴스가 참조하는 것을 강하게 유지하지 않는다.
unknown 객체는 Non-optional 타입이다 (항상 값을 갖기 때문에)
Strong
//자기 자신의 객체를 가지는 클래스
class Strong {
var strong: Strong? = nil //강한 참조 객체
}
//두개의 객체 변수 선언
var strong1: Strong? = Strong() //객체 변수
var strong2: Strong? = Strong() //객체 변수
//서로 강한 참조 (강한 순환 참조)
strong1?.strong = strong2
strong2?.strong = strong1
//두개의 객체 변수 메모리 해제
strong1 = nil
strong2 = nil
이 코드에서 잘 살펴보아야 할 것은 두개의 객체 변수는 마지막에 nil을 넣어 메모리 해제가 되었지만 각각의 객체는 강한 참조가 되었기 때문에 메모리 해제가 되지 못한다는 점입니다. 즉 객체인 strong은 nil을 넣어주지 못하고 객체 변수의 메모리를 해제하였다는 점입니다. 이때 객체에 접근할 방법도 없고 메모리를 해제할 방법도 없으니 메모리 누수가 일어나고 있습니다!
이러한 강한 순환 참조(Strong Reference Cycle)를 해결하는 방법에는 두가지가 있습니다. 두 가지 방법으로 약한 참조(weak reference)와 미소유 참조(unowned reference)입니다
Weak
weak (약한 참조) : 객체를 참조하고 reference count는 변화없음!
weak var reference1: Person? = Person(name: "Song") //약한 참조
//약한 참조이므로 바로 객체가 해제되어 nil이 됨
약한 참조는 강한 순환 참조를 해결하기 위해 사용되는 가장 보편적인 방법입니다. 주로 인스턴스의 생명주기가 짧을 때 사용하며 참조하고 있는 인스턴스를 강하게 유지하지 않기 때문에, 약한 참조로 참조하고 있는 동안에 인스턴스가 메모리 해제되는 것이 가능합니다. 그럼 위 코드를 다시 예시로 들어 살펴보죠!
//자기 자신의 객체를 가지는 클래스
class Strong {
weak var strong: Strong? = nil //약한 참조 객체
}
//두개의 객체 변수 선언
var strong1: Strong? = Strong() //객체 변수
var strong2: Strong? = Strong() //객체 변수
//서로 강한 참조 (강한 순환 참조)
strong1?.strong = strong2
strong2?.strong = strong1
//두 개의 객체 변수 메모리 해제
strong1 = nil
strong2 = nil
위의 코드에서 클래스의 객체 부분을 약한 참조로 바꾸어 보았습니다. 약한 참조를 하기 위해서는 변수의 앞에 weak를 붙여주면 됩니다.
여기서 중요하게 봐야 할 부분은 두 개의 객체 변수의 메모리가 해제되었을 때 강한 참조 일때와 달리 약한 참조를 한 객체는 ARC에서 자동으로 객체의 메모리까지 해제를 시켜준다는 점입니다! 그렇게 된다면 메모리 누수에 대한 문제점을 해결할 수 있겠군요..!
unowned 참조도 약한 참조와 마찬가지로 인스턴스가 참조하는 것을 강하게 유지하지 않습니다. 하지만 약한 참조와는 달리 미소유 참조는 다른 인스턴스와 같은 생명주기를 가지거나 더 긴 생명주기를 가질 때 사용합니다. 그리고 약한 참조와 가장 큰 차이점인 부분은 unowned 참조는 Optional에 사용하지 못한다는 점입니다. 따라서 nil값을 가질 수 없고 항상 값을 가지고 있어야 합니다.
참조하는 객체의 참조계수가 0이 되어 메모리가 해제되는 경우 약한 참조에서는 참조 값이 nil로 대체되지만 미소유 참조에서는 참조 값이 그대로 유지가 되죠.
참조 객체의 메모리 해제시
- 약한 참조 (weak) : 참조 값은 nil
- 미소유 참조 (unowned) : 참조 값은 유지
상황에 맞게 쓰자!
- strong : reference count를 증가시켜 ARC로 인한 메모리 해제를 피하고, 객체를 안전하게 사용하고자 할 때 사용.
- weak : 순환 참조에 의해 메모리 누수 문제를 막기 위해 사용.
- unowned : 객체의 life cycle이 명확하고 개발자에 의해 제어 가능이 명확한 경우, weak Optional타입 대신하여 사용.
'SwiftUI' 카테고리의 다른 글
Dictionary (0) | 2023.11.05 |
---|---|
Range (0) | 2023.11.05 |
Enumerations (0) | 2023.11.02 |
Generic Functions <T> (0) | 2023.10.31 |
Navigation Toolbar (0) | 2023.10.31 |