大器晩成

강함참조 사이클(Strong Reference Cycles Between Class Instances) 본문

iOS/Swift 문법

강함참조 사이클(Strong Reference Cycles Between Class Instances)

zerobugpark 2025. 3. 3. 15:56

두 클래스 인스턴스가 서로에 대한 강한 참조를 유지하여 각 인스턴스가 다른 인스턴스를 유지하는 경우 강한 참조사이클이라고 합니다. 이때, 메모리 누수가 발생됩니다.

 

Apple 공식문서 예제

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

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


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

 

 

  • john은 Person 인스턴스에 대한 강한 참조 사이클을 가지고 있으며, unit4A도 Apartment에 대한 강함 참조 사이클을 가지고 있습니다.
john!.apartment = unit4A
unit4A!.tenant = john

 

  • john인스턴스 내의 apatment는 unit4A를 참조하게 되면 Person 인스턴스는 Apartment에 대한 강한 참조 사이클이 발생합니다.
  • tenant는 Person에 대한 인스턴스를 참조하고 있으며, 이대 Apartment 인스턴스는  Person에 대한 강한 참조 사이클이 발생합니다. (각각 RC: 2)
john = nil
unit4A = nil
  • nil은 할당이 되지만 메모리에서 해제되지는 않습니다 (강한 참조 사이클 발생)
  • nil을 할당함으로써 각각의 RC 카운트는 1로 변하지만, 서로에 대한 참조에 대해서는 아직 해제를 함지 않았기 때문에, 메모리에서는 해제가 되지 않습니다.
  • 메모리 누수(Memory LeaK)의 상황이 발생

 

강한 참조 사이클 해결(Memory Leak의 해결 방안)

 

 

1. 약한 참조 (Weak Reference)

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

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

var john: Person?
var unit4A: Apartment?

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

john!.apartment = unit4A
unit4A!.tenant = john

 

 

john = nil
// Prints "John Appleseed is being deinitialized"
unit4A?.tenant // nil

 

unit4A = nil
// Prints "Apartment 4A is being deinitialized"

 

  • 위의 예제에서는 한쪽만 weak으로 선언해도 문제는 없습니다. 이때, 약한 참조의 경우 참조하고 있던 인스턴스가 사라지면 nil로 초기화됩니다.

 

2. 비소유 참조(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: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) is being deinitialized") }
}

var john: Customer?

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

  • john 변수에 의해 강한 참조를 끊을 때 비소유 customer 참조 때문에 Customer인스턴스에 대한 강한 참조가 발생하지 않습니다.
john = nil
// Prints "John Appleseed is being deinitialized"
// Prints "Card #1234567890123456 is being deinitialized"
  • 비소유 참조의 경우, 참조하고 있던 인스턴스가 사라지면, nil로 초기화되지 않습니다.

 

 

 

728x90