[Swift] ARC (Automatic Reference Counting), 강한참조(strong), 순환참조(retain cycle), 약한참조(weak), 미소유참조(unowned)

2024. 3. 29. 23:12iOS/iOS

728x90
반응형

👉 ARC (Automatic Reference Counting) 이란?

  • 힙은 메모리를 해제해주지 않으면 메모리누수가 발생하기 때문에 힙 영역의 메모리를 관리하는 방법
  • 객체가 더 이상 필요하지 않을 때 메모리에서 자동으로 해제한다.

👉 ARC  장/단점

장점

  • 개발자가 참조해제 시점을 파악할 수 있다.
  • 런타임시점에 추가 리소스를 발생하지 않는다.

단점

  • 순환참조 발생 시 영구적으로 메모리가 해제되지 않을 수 있다.

 

👉 RC (Reference Counting) 란?

  • 객체를 현재 누가 가지고 있는 지(참조하고 있는 지)를 숫자로 나타낸 것

👉 RC 계산 시점

  • 컴파일 타임에 언제 참조되고 언제 해제되는 지 결정되어 런타임때 그대로 실행한다.

✔️ 컴파일 / 런타임

  • 컴파일(Compile): 작성된 소스코드를 컴퓨터가 이해할 수 있는 기계어로 변환하는 과정
  • 런타임(Runtime): 컴파일된 프로그램이 실행될 때, 프로그램이 실행되는 동안 메모리할당, 자원관리, 라이브러리 호출하는 과정
프로그래밍 -> 컴파일 -> 런타임 -> 실행

 

👉 RC 증가/감소

증가 (+1)

  • 새로운 객체를 생성할 때
  • 기존 객체를 다른 변수에 대입할 때

감소 (-1)

  • 객체를 가리키던 변수가 메모리에서 해제될 때
  • 변수가 nil이 지정되었을 때
  • 변수에 다른 값을 대입했을 때
이를 강한참조(Strong)라고 한다.

 

반응형

 

 

👉 순환참조

  • 두 객체가 서로가 서로를 참조하고 있는 상태를 순환참조라고 한다.
  • 예시를 보면
class A {
    var name: String
    var b: B?
}

class B {
    var Name: String
    var a: A?
}
  • 서로의 프로퍼티에 서로의 인스턴스가 존재하고 있는 상태임을 나타낸다.
var aaa: A? = A(name: "aaa")	// A의 RC +1 (총 1)
var bbb: B? = B(name: "bbb")	// B의 RC +1 (총 1)

aaa?.b = bbb	// B의 RC +1 (총 2)
bbb?.a = aaa	// A의 RC +1 (총 2)
  • 서로가 서로를 참조하고 있는 상태가 된다.
  • 결과는 A, B의 RC 모두 2가 된다.
  • 이 때의 문제점은 두 객체에 nil값을 할당할 때 생긴다.
aaa = nil
bbb = nil
  • aaa가 스택영역에서 nil값이 되어 메모리에서 해제되니까 A의 RC가 -1 감소하게 된다.
  • 하지만 bbb?.a의 값이 aaa를 참조하여 증가하였기 때문에 A의 RC는 1이 된다.
  • aaa와 bbb 모두 nil 값이 되어 메모리 해제가 되어 해당 값에 접근하지 못한다.
  • 앱이 죽기전까지 메모리에 계속 남아있게 된다. 이를 순환참조에서 생기는 문제점이다.
  • 이를 해결하기 위해서 weak, unowned 를 사용한다.

 

✔️ 약한참조 (weak)

  • 약한참조는 RC를 증가시키지 않는다.
  • 참조하던 객체가 메모리에서 해제되면 자동으로 nil 값으로 할당되어 메모리에서 해제되는 방식이다.
class A {
    var name: String
    weak var b: B?
}

class B {
    var Name: String
    var a: A?
}
  • weak 키워드를 붙여서 사용한다.
  • 약한참조는 나중에 nil 값이 할당되기 때문에 무조건 옵셔널 타입이어야 한다.
var aaa: A? = A(name: "aaa")	// A의 RC +1 (총 1)
var bbb: B? = B(name: "bbb")	// B의 RC +1 (총 1)

aaa?.b = bbb	// b는 약한참조이기 때문에 RC 증가하지 않음, B의 RC +0 (총 1)
bbb?.a = aaa	// A의 RC +1 (총 2)
  • 약한참조는 한 경우에는 RC가 증가하지 않는다.
  • B를 참조하지만 B의 RC는 동일하다.
aaa = nil
bbb = nil
  • 다시 두 객체를 nil로 선언해주면 aaa와 bbb는 메모리에서 해제되고 RC는 각각 -1씩 감소한다.
  • 결과는 A의 RC는 총 1이되고 B의 RC는 총 0이 된다.
  • 그러면 ARC에 의해 RC가 0이 되면 B는 메모리에서 해제된다.
  • 이 때 B가 메모리에서 해제되는 순간 A에서 참조하고 있던 B의 값이 nil로 바뀌게 된다.
  • B의 값이 nil로 바뀌면서 A의 RC도 -1가 감소하게 되면서 총 0이 된다. 이로써 A도 메모리에서 해제되는 것이다.
  • 이러한 과정을 약한 순환참조라고 한다.

 

✔️ 미소유참조 (unowned)

  • 미소유참조는 약한참조랑 거의 비슷하다
  • 순환참조를 해결하기 위해서 사용되면서 RC를 증가시키지 않는다.
  • 차이점은 참조하던 객체가 메모리에서 해제된 경우 nil값으로 할당받지 않고 해제된 메모리주소를 계속 가지고 있다는 것이다.
  • 따라서 해제된 메모리주소에 접근하면 에러가 발생할 수 있다.

 


📂 정리

weak unowned
순환참조를 해결하기 위해 사용
RC 값을 증가하지 않음
참조하던 객체가 해제되면 자동으로 nil 할당 참조하던 객체가 해제되면 주소값을 가지고 있음

 

 

 

 

 

 

 

 

 

 

 

728x90
반응형