본문 바로가기

iOS

[iOS] ARC 란? (Automatic Reference Counting)

앱의 성능을 유지하기 위해서는 메모리 관리가 기본이자 필수적 입니다. 그렇기 때문에 코코아(Cocoa)에서 사용하는 메모리 관리모델을 이해할 필요가 있습니다. 바로 MRCARC 2가지 관리 모델입니다.

 

Cocoa란? 애플에서 제공하는 어플리케이션 개발 환경을 의미합니다.

macOS에서는 Cocoa, iOS에서는 Cocoa Touch 라고 부릅니다.

 

스택(Stack) 메모리에 저장된 데이터는 자동으로 제거되기 때문에 특별한 관리가 필요없습니다. 하지만 힙(Heap) 메모리에 저장된 데이터는 필요하지 않은 시점에 직접 제거해야만 합니다. 메모리 관리 모델은 힙에 저장된 데이터를 관리합니다.

 

Value Type: Structure, Enumeration, Tuple

Reference Type: Class, Closure

 

Value Type은 스택 메모리에, Reference Type은 힙 메모리에 저장됩니다.

언어를 기준으로 사용 가능한 메모리 관리 모델을 알아보자면,

 

  • Objective-C
    • MRC
    • ARC
  • Swift
    • ARC 

스위프트는 ARC 모델만 지원합니다.

여기서, MRC와 ARC에 대해 간략히 알아보도록 하겠습니다.

 

MRC에서 ARC로의 변화

 

MRCManual Reference Counting 의 약자로 개발자가 직접 메모리 관리하는 코드를 작성해야 합니다. 인스턴스의 기본 메소드인 retainrelease 를 호출하면서 말이죠. 이렇게 되면 코드양이 많아지고 메모리 오류 가능성이 높아지게 됩니다. 즉, 프로그램의 안정성이 낮아지게 됩니다.

 

ARCAutomatic Reference Counting의 약자입니다. 컴파일러가 알아서 메모리 관리 코드를 삽입해 줍니다. 코드의 양은 적어지고 프로그램의 안정성을 높일 수 있게되었습니다. ARC가 등장하면서 부터 거의 모든 프로젝트는 ARC를 사용한다고 봐도 무방합니다.

Reference Counting

인스턴스는 하나 이상의 소유자(Owner)가 있는 경우 메모리에 유지됩니다. 소유자가 없다면 그 즉시 메모리에서 제거됩니다. 제거 시점을 파악하기 위하여 소유자 수를 저장하는데 이것을 참조 카운트(Reference Count)라고 합니다.

 

참조 카운트가 1 이상이면 메모리에 유지되고 0 이 되면 메모리에서 제거됩니다. 클래스 인스턴스를 변수에 저장하면 변수가 소유자가되고 이 때 참조 카운트는 1이 증가하는 방식이죠. 마찬가지로 또 다른 변수에 저장하면 2가 됩니다. 코드 레벨로 보자면 해당 인스턴스의 retain 메소드를 호출하는 것과 같습니다.

 

인스턴스가 더 이상 필요하지 않다면 소유권을 포기해야하고 이것은 release 메소드와 같습니다. release 하면 참조 카운트가 1 감소하게 되는 것이죠. 결국 0이 되면 즉시 메모리에서 제거되게 됩니다.

 

참조 방식에는 3가지가 있습니다.

  • Strong
  • Weak
  • Unowned

참조 방식에 대해서는 다음 포스팅에서 활용 방법과 함께 자세히 다루도록 하겠습니다.

Strong Reference

기본적으로 인스턴스와 소유자는 강한 참조로 연결됩니다. 대상을 소유할 때마다 카운트가 1씩 증가하고 포기하면 1씩 감소된다는 말과 같습니다. 소유자가 존재하면 메모리에서 제거되지 않겠죠.

 

이제 예시를 통해 어떻게 메모리 관리가 되고 있는지 보겠습니다.

 

class Person {
    let name: String

    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }

    deinit {
        print("\(name) is being deinitialized")
    }
}

 

name 프로퍼티를 가지고 있는 Person 클래스를 선언하였습니다. 이제 Person 클래스로 인스턴스를 만들어 보겠습니다.

 

var person1: Person?
var person2: Person?
var person3: Person?

person1 = Person(name: "John") // 참조 카운트 1 증가 => 총 카운트 1
// Prints "John is being initialized"

person2 = person1 // 참조 카운트 1 증가 -> 총 카운트 2
person3 = person1 // 참조 카운트 1 증가 -> 총 카운트 3

// nil을 저장하는 것은 소유권을 포기하는 것과 같습니다.
// 즉시 강한 참조가 제거됩니다.
person1 = nil // 참조카운트 1 감소 -> 총 카운트 2
person2 = nil // 참조카운트 1 감소 -> 총 카운트 1
// 아직 1 이므로 여기까지는 인스턴스가 제거되지 않습니다.

person3 = nil // 참조카운트 1 감소 -> 총 카운트 0
// Prints "John is being deinitialized"
// 이 때 메모리에서 인스턴스가 해제됩니다.

정리

  • 메모리 관리모델에는 MRC와 ARC가 있습니다.
  • 스위프트는 기본적으로 ARC 입니다.
  • ARC는 컴파일러가 직접 retain, release 메소드를 넣어줍니다.
  • 참조 카운트는 소유자 수를 의미합니다.
  • 인스턴스의 메모리 관리는 참조 카운트를 이용합니다.

더 나아가기

강한 참조 사이클 (순환 참조) 해결하기 1탄

강한 참조 사이클 (순환 참조) 해결하기 2탄

태그