본문 바로가기
iOS/iOS

[iOS] ARC (Automatic Reference Counting)

by 0inn 2022. 9. 8.

ARC란 ?

ARC는 클래스 인스턴스가 더 이상 필요하지 않을 때 메모리를 자동으로 해제합니다.

즉, 힙(Heap)에 할당된 인스턴스의 메모리를 알아서 관리해줍니다.

참조타입(Reference)과 힙(Heap)

인스턴스, 클로저 등의 참조 타입은 자동으로 힙에 할당됩니다.

예를 들어, ImClass라는 클래스가 있고, student1이라는 인스턴스를 생성해봅시다.

 

class ImClass {
	var name: String?
    
    init(name: String?) {
    	self.name = name
    }
}

var student1 = ImClass(name: "0inn")
var student2 = student1

 

그러면 스택영역에는 student1(주소값)가 할당되고, 힙에는 실제 ImClass의 인스턴스(name: "0inn")가 할당됩니다.

스택에 있는 student1은 힙 영역에 있는 인스턴스를 참조하고 있는 형태로 student1 안에는 힙에 할당된 인스턴스의 주소값이 들어갑니다.

참조의 형태이기 때문에 인스턴스가 복사되지 않고 student2는 같은 힙 영역의 인스턴스(name: "0inn")를 가리키고 있게됩니다.

 

여기서 중요한 점 !

힙은 사용하고 난 후에 반드시 메모리 해제를 해줘야 합니다.

그런데 여태까지 인스턴스를 사용해봐서 알겠지만 실제로 직접 메모리를 해제해 준 적이 없습니다.

그렇다면 스택에 있는 student1과 student2가 함수 종료 시점에서 사라지면 누가 메모리 해제를 해줄까요 ?

이 때, ARC가 메모리 해제를 해줍니다.

GC vs. RC

힙 영역의 메모리를 관리하는 방법에는 GC와 RC가 있습니다. (ARC는 RC에 포함)

 

  GC RC
참조 계산 시점 런타임 컴파일 타임
장점 인스턴스가 해제될 확률 높음 참조 해제 시점 파악 가능
런타임 시점에 추가 리소스 발생 X
단점 - 참조 해제 시점 파악 불가능
- 런타임 시점에 계속 추적하는 추가 리소스가    필요해 성능 저하 발생 가능
순환 참조가 발생하면
영구적으로 메모리가 해제되지 않을 수 있음

MRC(MRR) vs. ARC

Manual vs. Automatic 의 차이로 MRC는 힙에 메모리를 직접 할당 및 해제를 해줍니다.

ARC의 메모리 관리 방법

메모리 참조 횟수(RC)를 계산하여 참조 횟수가 0이 되면 더 이상 사용하지 않는 메모리가 생각하여 해제합니다.

여기서 RC는 이 인스턴스를 현재 누가 참조하고 있는지를 숫자로 나타낸 것입니다.

예를 들어, 참조 횟수가 10이라면 해당 인스턴스를 10군데에서 참조하고 있다는 뜻이고, 0이면 아무데서도 참조하지 않으니 메모리를 해제해도 된다는 의미입니다.

그렇기 때문에 모든 인스턴스를 자신의 RC 값을 가지며 인스턴스가 생성될 때 힙에 같이 저장됩니다.

RC +

참조 횟수가 +1되는 순간은 인스턴스의 주소값을 변수에 할당할 때입니다.

 

  1. 인스턴스를 새로 생성할 때
  2. 기존 인스턴스를 다른 변수에 대입할 때

RC -

  1. 인스턴스를 가르키던 변수가 메모리에서 해제되었을 때
  2. nil이 지정되었을 때
  3. 변수에 다른 값을 대입한 경우
var student1: ImClass = .init(name: "0inn")	// student1 RC: 1
var student2: ImClass = .init(name: "jenny")	// student2 RC: 1

student1 = student2	// student1 RC: 0 [메모리 해제]
			// student2 RC: 2

 

  4. 프로퍼티의 경우, 속해 있는 클래스 인스턴스가 메모리에서 해제될 때

 

class Info {
    var name: String?
    
    init(name: String?) {
    	self.name = name
    }
}

class Student {
    var grade: Int?
    var info = Info? = .init(name: "0inn")
    
    init(grade: int?) {
    	self.grade = grade
    }
}

let student1: Student? = .init(grade: "1")	// student1의 Student RC: 1 & info RC: 1
student1 = nil	// student1의 Student RC: 0 & info RC: 0

 

Student라는 클래스 안에 info라는 클래스 인스턴스가 프로퍼티로 존재합니다.

그렇기 때문에 Student 인스턴스 student1을 생성하면 student1과 info의 RC 모두 증가합니다.

이 때, student1에 nil을 할당하면 student1이 가리키던 Student 인스턴스의 RC가 1 감소하여 0이 되어 메모리에서 해제됩니다.

Student 인스턴스가 메모리에서 해제되니 info가 가리키던 Info 인스턴스의 RC도 1 감소하여 0이 되어 메모리에서 해제됩니다.

 


참고

https://babbab2.tistory.com/26?category=831129

 

 

'iOS > iOS' 카테고리의 다른 글

[iOS] UIView.frame과 UIView.bounds  (0) 2023.09.04
[iOS] URLSession  (0) 2022.09.13
[iOS] strong / weak / unowned / 순환 참조  (0) 2022.09.08