Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Archives
Today
Total
관리 메뉴

Jupyo's Daily Story

ARC (Automatic Reference Counting) 본문

Swift

ARC (Automatic Reference Counting)

JangJupyo 2024. 10. 2. 18:33
728x90
반응형

ARC는 자동으로 메모리 관리를 수행하는 방식입니다. 객체가 더 이상 필요 없을 때 메모리를 자동으로 해제하며, 주로 클래스 인스턴스에 적용됩니다. ARC는 참조 카운트를 사용하여 객체가 몇 군데에서 참조되고 있는지 추적합니다. 이때 참조 카운트가 0이 되면 해당 객체의 메모리가 해제됩니다.

 

ARC의 동작 방식

클래스 인스턴스를 생성하면 ARC는 해당 인스턴스의 메모리를 할당하고, 이 인스턴스를 참조하는 각 프로퍼티나 변수는 참조 카운트(Reference Count)를 증가시킵니다. 참조 카운트가 0이 되면 메모리를 자동으로 해제합니다.

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

var person1: Person? = Person(name: "John")
person1 = nil  // 메모리 해제

위의 예에서, Person 인스턴스가 생성되면 메모리가 할당되며 참조 카운트가 1이 됩니다.  person1 변수를 nil 로 설정하면 참조 카운트가 0이 되어 메모리가 해제되고 deinit 메서드가 호출됩니다.

 

참조의 유형

ARC는 다음 세 가지 유형의 참조를 지원하여 순환 참조 문제를 해결합니다.

  • 강한 참조(Strong Referecne): 기본적으로 클래스 인스턴스를 참조할 때, 참조 카운트가 증가합니다. 참조 카운트가 0이 될 때까지 메모리가 해제되지 않습니다.
  • 약한 참조(Weak Reference): 참조 카운트를 증가시키지 않는 참조입니다. 참조하는 인스턴스가 해제되면 자동으로 nil이 됩니다. 옵셔널 타입으로 선언됩니다.
  • 미소유 참조(Unowned Referecne): 약한 참조처럼 참조 카운트를 증가시키지 않지만, 참조하는 인스턴스가 해제될 때도 nil로 변하지 않는 비옵셔널 참조입니다. 참조한 인스턴스가 해제된 후 접근하면 런타임 오류가 발생할 수 있습니다.

 

강한 순환 참조(Circular Reference) 문제

서로 다른 클래스 인스턴스가 강한 참조로 서로를 참조하는 경우, 참조 카운트가 0이 되지 않아 메모리가 해제되지 않는 메모리 누수 문제가 발생합니다.

class Person {
    let name: String
    var apartment: Apartment?  // 강한 참조
    init(name: String) { self.name = name }
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    var tenant: Person?  // 강한 참조
    init(unit: String) { self.unit = unit }
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person? = Person(name: "John")
var unit4A: Apartment? = Apartment(unit: "4A")

john?.apartment = unit4A
unit4A?.tenant = john

john = nil  // 메모리 해제되지 않음
unit4A = nil  // 메모리 해제되지 않음

Person과 Apartment는 서로를 강하게 참조하고 있기 때문에 참조 카운트가 0이 되지 않고 메모리 해제가 되지 않습니다.

 

해결책: 약한 참조(Weak Reference)와 미소유 참조(Unowned Reference)

강한 순환 참조를 방지하기 위해 하나의 참조를 약한 참조 또는 미소유 참조로 변경해야 합니다. 이렇게 하면 객체 간의 참조는 유지되지만, 참조 카운트는 증가하지 않아 순환 참조가 발생하지 않습니다.

class Apartment {
    let unit: String
    weak var tenant: Person?  // 약한 참조
    init(unit: String) { self.unit = unit }
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person? = Person(name: "John")
var unit4A: Apartment? = Apartment(unit: "4A")

john?.apartment = unit4A
unit4A?.tenant = john

john = nil  // 메모리 해제
unit4A = nil  // 메모리 해제

 

미소유 참조(Unowned Reference)

미소유 참조는 메모리 누수를 막기 위해 사용할 수 있지만, 참조하는 객체가 해제된 후에도 참조가 남아 있는 경우에는 런타임 오류가 발생할 수 있습니다. 주로 부모-자식 관계에서 부모가 먼저 해제되지 않는 것을 보장할 수 있는 경우에 사용합니다.

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
    deinit { print("\(name) is being deinitialized") }
}

class CreditCard {
    let number: Int
    unowned let customer: Customer  // 미소유 참조
    init(number: Int, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("CreditCard #\(number) is being deinitialized") }
}

var john: Customer? = Customer(name: "John")
john?.card = CreditCard(number: 1234, customer: john!)

john = nil  // Customer와 CreditCard 모두 해제됨

 

ARC와 성능

ARC는 대부분의 경우 메모리 관리에 매우 효율적이지만, 강한 순환 참조 문제가 발생할 수 있으므로 주의해야 합니다. 이를 위해 약한 참조미소유 참조를 적절히 사용하여 메모리 누수를 방지할 수 있습니다.

 

결론

ARC는 Swift에서 메모리 관리를 자동화해 개발자가 메모리 관리에 신경 쓰지 않도록 도와줍니다. 하지만 강한 참조 순환이 발생할 수 있는 구조에서는 약한 참조미소유 참조를 적절히 사용해 메모리 누수 문제를 해결하는 것이 중요합니다.

반응형

'Swift' 카테고리의 다른 글

콜렉션 타입(Collection Type) - 집합(Set)  (4) 2024.10.07
콜렉션 타입(Collection Type) - 배열(Array)  (2) 2024.10.07
클로저 (Closures)  (3) 2024.10.02
Codable  (0) 2024.09.30
OperationQueue  (0) 2024.09.26