본문 바로가기
개린이 이야기

ARC(Auto Reference Counting)에 관하여

by iOS 개린이 2022. 9. 27.

-우리는 개발을 할 때 메모리 관리를 통해 앱의 성능을 좋게 만들어주어야 한다.

메모리 영역 중 heap 영역의 특성은 class나 closure 등의 참조형 자료들이 머무는 공간이면서, 개발자가 동적으로 메모리 관리를 해주어야 하는 영역이다.

heap영역을 관리하지 않으면, 적절한 시점에 메모리가 소멸되지 않고 -> 메모리가 낭비되고 -> 성능 저하로 이어진다.

 

따라서 앱의 성능을 위해 메모리 관리가 필요하고, heap 영역을 효율적으로 관리해주기 위해서 ARC라는 도구가 필요한 것이다.  

 

오늘은 IOS개발자라면 필수로 이해해야하는 ARC에 관하여 deep diving 할 것이다.

 

ARC(Auto Reference Counting)

-ARC 이름의 뜻 그대로 reference counting을 통해 자동으로 메모리를 관리해주는 도구를 말한다.

-reference counting의 방식은 heap 영역에 참조형 자료들이 얼마나 참조되고 있는 지 참조 횟수를 count하고 이에 따라서 메모리를 할당하거나 해제시키는 것이다.

-reference count가 0이 되면 메모리가 해제된다.

-class의 instance가 필요하지 않을 때 남아있는 메모리를 자동으로 해제해준다.

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

 

-컴파일 단계에서 코드 분석을 통해 언제 참조되고 해제되는 지에 대한 코드(retain, release 등)를 적절한 위치에 넣어주고, 런타임 단계에서 코드가 실행된다. 

런타임에 참조와 해제에 관한 코드(retain, release 등)를 통해 reference counting을 하다가 count가 0이 될 때 deinit 메소드와 함께 메모리를 소멸시킨다.

*retain : 객체가 메모리에서 해제되지 않도록 reference count를 증가시켜주는 함수

*release : 객체가 더 이상 필요하지 않을 때 reference count를 감소시켜주는 함수

 

-ARC가 코드를 분석해서 적절한 위치에 메모리 관리 코드를 넣어준다고 했는데 어떤 방식으로 할까?

 ARC는 명확한 작동 규칙을 가지고 컴파일 단계에서 할당과 해제 시점을 미리 예측한다. 그래서 ARC의 규칙을 모르고 사용하면 메모리 관리가 제대로 이루어지지 않을 수 있다.

 

Reference Counting 예시

참조 횟수가 추가되는 상황

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

var reference1: MyClass? = MyClass(name: "Instance")  // 참조 카운트: 1
var reference2: MyClass? = reference1  // 참조 카운트: 2
var reference3: MyClass? = reference1  // 참조 카운트: 3

 

-위의 코드에서 'MyClass' 라는 클래스를 선언하고, 'reference1' 이라는 변수를 사용해 인스턴스를 생성했다.

이 시점에서 참조 카운트는 1이다.

후에 'reference2', 'reference3' 이 'reference1' 을 참조하게 되면서 참조 카운트가 각각 2, 3으로 증가한다.

 

메모리적 관점에서 보자.

 

'reference1', 'reference2', 'reference3' 은 모두 스택 메모리에 저장된다. 

이들은 모두  'MyClass'의 인스턴스를 참조하는 포인터 라고 생각할 수 있다. 

각각 힙 메모리에 저장된 'MyClass'의 인스턴스를 가리키는 메모리 주소를 저장하고 있다.

 

이렇게 각 참조 포인터가 생성될 때마다 ARC는 참조 카운트를 증가시킨다.

스택 메모리는 함수 호출과 함께 할당되고 해제되는 구조를 가지고 있다.

 

 

참조 횟수가 감소하는 상황

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

var reference1: MyClass? = MyClass(name: "Instance")  // 참조 카운트: 1
var reference2: MyClass? = reference1  // 참조 카운트: 2
var reference3: MyClass? = reference1  // 참조 카운트: 3

reference2 = nil //참조 카운트: 2
reference3 = nil //참조 카운트: 1
reference1 = nil //참조 카운트: 0, 인스턴스 해제

 

각 참조 포인터가 nil로 설정될 때마다, 해당 참조는 더 이상 힙 메모리에 있는 인스턴스를 가리키지 않게 된다.

따라서 ARC는 참조 카운트를 감소시키고, 참조 카운트가 0이 되면 힙 메모리에 있는 인스턴스를 해제한다.

 

 

 

참고

iOS) 메모리 관리 (1/3) - ARC(Automatic Reference Counting) (tistory.com)

[Swift] ARC 뿌시기. ARC.. 들어는 봤습니다만?ㅎ | by naljin | Medium

[Swift] ARC (1); ARC의 개념, Retain Count (tistory.com)