객체가 서로를 참조하여 변수의 참조에 nil을 할당해도 메모리가 해제가 되지 않는 참조 사이클
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(name: "John Appleseed")
var unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
강한 순환 참조
john 인스턴스의 apartment 프로퍼티로 unit4A를 가리킴 → Person 인스턴스 reference count: 2unit4A 인스턴스의 tenant 프로퍼티로 john를 가리킴 → Apartment 인스턴스 reference count: 2
여기서 다시 ARC의 메모리 관리 방식에 대해서 기억 필요
john = nil
unit4A = nil
john = nil 코드 실행 후, Person 인스턴스 reference count 2 → 1
unit4A = nil 코드 실행 후, Apartment 인스턴스 reference count 2 → 1
→ 참조 카운트가 1이므로 메모리에 해제되지 않는다.
이렇게 되면 메모리가 해제되지 않고 해제할 방법도 없음 → 메모리 누수 발생
강한 참조 사이클로 인한 메모리 누수를 해결할 방안은 2가지다.
🔥 weak, unowned 키워드를 사용하는 것이다.해당 키워드를 사용하면 가르키는 인스턴스의 reference count가 올라가지 않는다. → 강한 참조하지 않음
weak로 선언하면 메모리에 인스턴스를 유지시키지 못 함
nil로 자동 설정nil로 변할 수 있으므로 weak 프로퍼티는 옵셔널 타입 변수로 선언되어야 함Note
프로퍼티 관찰자는 ARC가 약한 참조로 설정할 때 호출되지 않는다.
- 아래 코드를 실행해보면 weak 프로퍼티는 observer가 실행되지 않음을 알 수 있다.
class Person {
let name: String
init(name: String) { self.name = name }
weak var apartment: Apartment? {
willSet {
print("현재 이름 = \(owner?.name), 바뀔 이름 = \(newValue)")
}
didSet {
print("현재 이6름 = \(owner?.name), 바뀌기 전 이름 = \(oldValue)")
}
}
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(name: "John Appleseed")
var unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
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? // weak 프로퍼티로 선언됨
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"
위와 같이 john 인스턴스에 nil을 할당하면 Person 인스턴스 reference count는 0이 되어 메모리에서 해제된다. 따라서 Apartment 인스턴스를 가리키는 참조 하나도 자연스레 없어진다.

unit4A까지 nil을 할당하면 남아있는 Apartment 인스턴스 참조가 0이 되면서 메모리에서 해제됩니다.
그렇게 자연스레 메모리 누수를 방지할 수 있습니다.
unit4A = nil
// Prints "unit4A Appleseed is being deinitialized"

unowned는 weak 보다 다른 인스턴스의 수명이 같거나 더 긴 경우 사용
unowned reference는 항상 값을 갖도록 예상 → 참조하고 있던 인스턴스가 사라지면, nil로 초기화 되지 않음
class Dog1 {
var name: String
unowned var owner: Person1?
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
class Person1 {
var name: String
unowned var pet: Dog1?
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
var bori1: Dog1? = Dog1(name: "보리1")
var gildong1: Person1? = Person1(name: "홍길동1")
// 강한 참조 사이클이 일어나지 않음
bori1?.owner = gildong1
gildong1?.pet = bori1
bori1?.owner = nil// 에러발생하는 케이스
gildong1 = nil
bori1?.owner // nil로 초기화 되지 않음 에러 발생
weak
unowned