[iOS / Swift] 메모리 참조 방법 (strong, weak, unowned)

프린스송·2021년 6월 3일
3

iOS 앱 개발

목록 보기
8/23
post-thumbnail

안녕하세요~~ 오늘은 저번 시간에 이어서 메모리에 관한 공부를 해보겠습니다. 저번 포스팅에서는 메모리 관리인 ARC에 대해서 공부를 했다면 이번에는 메모리 참조 방법에 대한 내용을 다루겠습니다. 그럼 자세히 알아보도록 하죠!


메모리 참조?

저번 포스팅에서 공부했듯이 iOS에서는 ARC를 활용해 앱의 메모리 사용을 추적하고 관리합니다. 메모리가 참조 되고 해제 될때 ARC는 자동으로 이것을 관리해준다는 내용도 다 확인해 보았구요! ARC에 대해 간략히 정리하면 객체에 대해 강한 참조가 남아 있는한 해당 객체는 메모리 해제가 되지 않게 됩니다.

하지만 이렇게 강한 참조만 쓴다면 일일이 메모리 해제를 위해 신경 써야하고 그러지 못하게 된다면 엄청난 메모리가 낭비될 것입니다. 그래서 iOS에는 weak, unowned을 이용하여 조금 더 효율적이게 메모리를 관리 할 수 있습니다. 그럼 이 내용에 대해 더 자세히 알아보도록 하겠습니다.


strong (강한 참조)

strong (강한 참조) : 객체를 참조하고 reference count를 증가시킴!

var reference1: Person? = Person(name: "Song") //강한 참조
reference1 = nil //메모리 해제

이전 포스팅에서 살펴보았듯이 강한 참조를 하게 되면 reference count가 증가 하게 됩니다. 이때 우리는 해당 객체에 nil을 넣어주어야 메모리가 해제되는 것도 확인을 했습니다.
하지만 여기서 두 개의 객체가 상호 참조하는 경우와 같은 강한 순환 참조(Strong Reference Cycles)가 만들어 질 수 있습니다. 이 경우 reference count가 0이 되지 못해 메모리 누수(Memory Leak)가 발생됩니다. 간단한 코드를 통해 살펴보죠.

//자기 자신의 객체를 가지는 클래스
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 (미소유 참조) : 객체를 참조하고 reference count는 변화없음!

미소유 참조도 약한 참조와 마찬가지로 인스턴스가 참조하는 것을 강하게 유지하지 않습니다. 하지만 약한 참조와는 달리 미소유 참조는 다른 인스턴스와 같은 생명주기를 가지거나 더 긴 생명주기를 가질 때 사용합니다. 그리고 약한 참조와 가장 큰 차이점인 부분은 미소유 참조는 Optional에 사용하지 못한다는 점입니다. 따라서 nil값을 가질 수 없고 항상 값을 가지고 있어야 합니다.

참조하는 객체의 참조계수가 0이 되어 메모리가 해제되는 경우 약한 참조에서는 참조 값이 nil로 대체되지만 미소유 참조에서는 참조 값이 그대로 유지가 되죠.

참조 객체의 메모리 해제시

  • 약한 참조 (weak) : 참조 값은 nil
  • 미소유 참조 (unowned) : 참조 값은 유지

상황에 맞게 쓰자!

  • strong : reference count를 증가시켜 ARC로 인한 메모리 해제를 피하고, 객체를 안전하게 사용하고자 할 때 사용.
  • weak : 순환 참조에 의해 메모리 누수 문제를 막기 위해 사용.
  • unowned : 객체의 life cycle이 명확하고 개발자에 의해 제어 가능이 명확한 경우, weak Optional타입 대신하여 사용.

오늘은 메모리 참조 방법에 대해서 공부를 해보았습니다. 하지만 조금 남는 이 찝찝함을 무엇일까요.. 완벽히 알 것 같으면서도 조금은 부족한 느낌... 아무래도 swift에 대한 더 정교한 코딩을 해봐야지 완벽히 숙지를 할 것 같군요. 이것 역시 시간과 경험이 해결해 줄 거라고 믿고 있습니다. 그럼 오늘 하루도 수고 많으셨습니다!

profile
[iOS / Swift] [React Native]

0개의 댓글