class 와 같은 인스턴스는 참조 형식 인스턴스 입니다.
두 개의 참조 형식 인스턴스가 서로 강한 참조(Strong reference)를 한다면 순환 참조가 발생합니다.
이는 두 인스턴스가 메모리에서 해제되어도 서로에 대한 참조가 해제되지 않기 때문에 메모리에 남게되고 심해지면 메모리 릭 현상이 발생합니다.
이러한 순환참조를 해결하기 위해 참조할 때 weak, unowned 키워드를 사용합니다.
먼저 순환참조가 발생하는 예시입니다.
// Person Class
class Person {
var name: String
var pet: Pet?
init(name: String) {
self.name = name
print("Person init")
}
deinit {
print("Person deinit")
}
}
// Pet Class
class Pet {
var owner: Person?
init() {
print("Pet init")
}
deinit {
print("Pet deinit")
}
}
// 두 개의 인스턴스 생성
var person: Person?
var pet: Pet?
person = Person(name: "cheolsu")
pet = Pet()
// 각 인스턴스의 속성을 다른 인스턴스가 강한 참조하도록 설정
person?.pet = pet
pet?.owner = person
// 메모리에서 해제한다.
person = nil
pet = nil
// 출력 결과
Person init
Pet init
-> Person 클래스와 Pet 클래스의 메모리 해제가 이루어지지 않음
person, pet 인스턴스를 모두 메모리에서 해제하여도 deinit 이 되지 않았습니다.
결국 서로의 참조 상태는 메모리에 남아있습니다.
약한 참조 weak
선언하는 프로퍼티 앞에 weak 키워드를 붙여 약한 참조하도록 명시적으로 설정하는 방법입니다.
class Pet {
weak var owner: Person?
init() {
print("Pet init")
}
deinit {
print("Pet deinit")
}
}
weak 키워드가 붙여진 프로퍼티는 다음과 같은 특징을 갖고 있습니다.
- 참조하는 인스턴스의 RC(Reference Count)를 증가시키지 않습니다.
- 참조하는 인스턴스가 메모리에서 해제되면 nil이 할당되어 메모리에서 해제시킵니다.
- 런타임중 nil이 할당되는 경우가 존재할 수 있으므로 옵셔널 형식입니다.
unowned
두번째로는 unowned 키워드를 선언하는 프로퍼티 앞에 붙이는 방법이 있습니다.
class Pet {
unowned var owner: Person
init() {
print("Pet init")
}
deinit {
print("Pet deinit")
}
}
unowned 키워드가 붙여진 프로퍼티는 다음과 같은 특징을 갖고 있습니다.
- 참조하는 인스턴스의 RC(Reference Count)를 증가시키지 않습니다.
- 참조하는 인스턴스가 메모리에서 해제되어도 여전히 Heap영역의 인스턴스를 가리키는 포인터가 됩니다.
따라서 unowned은 옵셔널 형식이 아니게 됩니다.
weak와 unowned의 차이점은 참조하는 인스턴스가 메모리에서 해제될 때 나타납니다.
weak은 nil이 되지만, unowned은 여전히 포인터로 남습니다.
따라서 참조되는 인스턴스가 메모리에 할당되는 기간이 참조하는 인스턴스보다 짧다면 weak을 사용하는 편이 좋고,
참조되는 인스턴스가 메모리에 할당되는 기간이 참조하는 인스턴스보다 같거나 길다면, 또는 필연적인 관계라면 unowned를 사용하는게 좋습니다.
'Swift' 카테고리의 다른 글
숫자 야구게임을 프로토콜지향 프로그래밍으로 리팩토링 해보기 (0) | 2025.03.12 |
---|---|
비동기 프로그래밍 DispatchQueue (0) | 2025.02.18 |
Swift 자료구조 (0) | 2025.02.13 |