ARC(Auto Reference Counting)

12655 단어 ARCswiftRCGCiOSARC

ARC(Auto Reference Counting)

ARC 설명

  • 앱의 메모리 사용량을 추적하고 관리합니다.
  • 참조 카운트(Reference Count)를 통해 힙 영역의 메모리를 관리합니다.
  • 해당 인스턴스가 더 이상 필요하지 않을 때(참조 카운트가 0이 됐을 때) 클래스 인스턴스에서 사용하는 메모리를 자동으로 해제합니다.
  • Complie TIme에 참조되고 해제되는 시점이 결정이 되고 런타임에서 결정된 대로 실행이 됩니다.
  • 2011년 이전의 낮은 버전의 Object-C에서는 개발자가 수동으로 메모리를 관리해주었습니다. MRC(Manual Reference Counting)라고 부릅니다.

GC vs RC

힙 메모리를 관리하는 방법은 두 가지가 있습니다.
GC(Garbage Collection)와 RC(Reference Count)가 있습니다.
JAVA(AOS)에서는 GC를 사용하고 Swift(iOS)에서 RC를 사용합니다.

GC와 RC 모두 사용하지 않는 객체를 해제하기 위한 수단입니다. 하지만 둘의 동작방식에 차이가 있습니다.

GC (Garbage Collection)

  • GC는 Mark-and-sweep 알고리즘을 기반으로 동작합니다. Garbage Collector가 GC root를 순회하면서 참조할 수 있는 모든 객체를 마킹합니다.
  • 이후 불특정한 시간에 마킹되지 않은(더 이상 필요없는) 모든 객체를 찾아 지웁니다.
  • 런타임 시점에서 주기적으로 참조를 추적하여 사용하지 않는 인스턴스를 해제합니다.

장점

  • RC만큼 메모리 누수가 되는 순환 참조에 대해서 고려하지 않아도 됩니다. (물론 발생은 하지만 .. RC만큼은 아닙니다..)

단점

  • 참조해제 되는 시점을 예측할 수 없습니다.
  • 런타임 때 참조를 추적하는데에 리소스가 사용되기 때문에 성능저하가 있을 수 있습니다.
  • 아주 예전에 단말의 성능이 좋지 않을 때는 한번에 많은 메모리를 지우거나 할 때 버벅거렸습니다.

RC (Reference Count)

  • RC는 ARC의 RC입니다.
  • 각 오브젝트의 Reference Counting을 통해서 카운트가 0이 되면 메모리를 해제하는 방식입니다.
  • 컴파일 시점에 참조 시점과 해제 시점이 결정되고 런타임 때 그대로 실행됩니다.

장점

  • 참조해제 되는 시점을 예측할 수 있습니다.
  • 런타임 시점에 추가적인 리소스가 필요하지 않습니다.

단점

  • 순환참조가 발생할 확률이 GC에 비해 많다.
  • 순환참조가 발생하면 영구적으로 메모리가 해제 되지 않게 됩니다.

ARC 동작

이제부터는 조금은 복잡한 이야기를 하려고 합니다!
ARC는 RC(Reference Count)로 메모리의 참조 카운트를 통해 메모리를 관리한다고 했었습니다.

예제를 보면서 설명하겠습니다.

RC가 증가할 때

인스턴스의 주소값을 변수에 할당하게 될 때 참조 카운트가 올라가게 됩니다.

class Person {
    let name: String
    
    init(name: String) {
        self.name = name
        print("init")
    }
    
    deinit {
        print("deinit")
    }
}

var hoBahk = Person(name: "Hohahk") // RC +1, Instance RC: 1
var hoBahk2 = hoBahk // RC +1, Instance RC: 2
var hoBahk3 = hoBahk // RC +1, Instance RC: 3

처음에 Person의 새로운 인스턴스인 hoBahk 인스턴스를 만들었을 때 RC가 1 증가합니다.
그리고 hoBahk인스턴스를 hoBahk2라는 변수에 대입할 때 RC가 또 1 증가하게 되어 RC가 2가 됩니다.
hoBahk3에도 Person의 인스턴스를 할당하게 되면 RC 1이 또 증가하기 떄문에 RC가 3이 됩니다.

RC가 감소할 때

RC가 감소할 때 예제를 보겠습니다!

1. 인스턴스에 nil을 할당 할 때 RC가 감소하게 됩니다.

class Person {
    let name: String
    
    init(name: String) {
        self.name = name
        print("init")
    }
    
    deinit {
        print("deinit")
    }
}

var hoBahk: Person? = Person(name: "Hohahk") // RC +1, Instance RC: 1
var hoBahk2 = hoBahk // RC +1, Instance RC: 2

hoBahk = nil // RC -1 , Instance RC: 1
hoBahk2 = nil // RC -1 , Instance RC: 0 (메모리 해제)

2. 인스턴스를 할당한 변수에 다른 값을 할당하게 되면 해당 인스턴스의 RC는 감소합니다.

var hoBahk: Person = Person(name: "Hohahk") // hoBahk Instance RC 1
var lisa = Person(name: "lisa")  // lisa Instance RC 1

hoBahk = lisa // hoBahk Instance RC 0(메모리 해제), lisa Instance RC 2

3. 인스턴스를 가리키던 변수가 메모리에서 해제어도 RC는 감소합니다.

func makeClonePerson(_ person: Person) {
    // 함수 내부에 들어온 당시에는 변수 clone에도 인스턴스 person을 할당하기 때문에 RC가 1 증가하여 2가 된다.
    let clone = person // Instance RC: 2
    // 함수를 나가게 되면 변수는 clone은 메모리에서 해제 되기 때문에 RC가 1 감소하여 1이 된다.
}

var hoBahk: Person = Person(name: "Hohahk") Instance RC: 1
makeClonePerson(hoBahk) Instance RC: 1

4. 다른 클래스의 프로퍼티로 속해 있을 때, 해당 클래스의 인스턴스가 메모리에서 해재될 때 RC가 감소하게됩니다.

class Person {
    let name: String
    let physicalSize = PhysicalSize(height: 180.3, weight: 75.1)
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("deinit")
    }
}

class PhysicalSize {
    let height: Double
    let weight: Double
    
    init (height: Double, weight: Double) {
        self.height = height
        self.weight = weight
    }
    
    deinit {
        print("deinit")
    }
}

var hoBahk: Person? = Person(name: "Hohahk")
hoBahk = nil // Person, PhysicalSize 모두 deinit

먼저 GC, RC에 대해 알아보고 ARC의 동작 방법에 대해서 알아보았습니다.

다음에는 ARC의 문제졈인 순환참조에 대해서 알아보겠습니다..!

감사합니다~

좋은 웹페이지 즐겨찾기