

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