순환참조

Little_Machine_Human_·2025년 1월 24일
post-thumbnail

Swift에서 순환참조(Retain Cycle)는 두 객체가 서로를 강하게 참조하여 메모리에서 해제되지 않는 현상을 말한다.

가장 흔한 예시는 클래스 간의 관계에서 발생하는데, 다음과 같은 상황이다.

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

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

이렇게 코드를 작성하고 다음과 같이 사용하면 순환참조가 발생한다.

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

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

john = nil
unit4A = nil

위 코드에서 john과 unit4A를 nil로 설정해도 deinit이 호출되지 않는다.
왜냐하면 각 객체가 서로를 강하게 참조하고 있기 때문이다.

이를 해결하기 위해서는 다음과 같은 방법들을 사용할 수 있다.

weak 참조: 약한 참조를 사용하면 참조 카운트를 증가시키지 않는다.

class Apartment {
    let number: String
    weak var tenant: Person?  // weak 키워드 사용
    ...
}

unowned 참조: 참조하는 인스턴스가 항상 존재한다고 확신할 때 사용한다.

class Customer {
    let name: String
    var card: CreditCard?
    ...
}

class CreditCard {
    let number: String
    unowned let customer: Customer  // unowned 키워드 사용
    ...
}

클로저에서는 캡처 리스트를 사용해서 약한 참조를 만들 수 있다.

class ViewController {
    var handler: (() -> Void)?
    
    func setupHandler() {
        handler = { [weak self] in
            self?.doSomething()
        }
    }
}

이러한 방법들을 사용하면 순환참조를 피하고 메모리 누수를 방지할 수 있어.
특히 델리게이트 패턴이나 클로저를 사용할 때는 항상 순환참조 가능성을 고려해야 해야한다.

profile
while(true){ 가족(); 건강(); 자기개발(); }

0개의 댓글