J
[Swift] weak, unowned 본문
1. weak와 unowned란?
Swift에서는 메모리 관리를 위해 강한 참조(Strong Reference), 약한 참조(Weak Reference), 비소유 참조(Unowned Reference)를 제공.
weak와 unowned는 참조 타입 사이에서 강한 순환 참조(Strong Reference Cycle)를 방지하는 데 사용.
둘 다 ARC(Automatic Reference Counting)의 메모리 누수 문제를 해결하는 데 중요한 역할.
2. weak와 unowned의 공통점
- 강한 참조(Strong Reference)를 생성하지 않음.
- 참조하는 인스턴스가 메모리에서 해제될 수 있도록 함.
- 강한 순환 참조를 방지하여 메모리 누수를 줄임.
3. weak와 unowned의 차이점
weak | unowned | |
참조 대상 | Optional로 선언됨 | Non-Optional로 선언됨 |
메모리 해제 시 동작 | 참조 대상이 해제되면 nil로 설정 | 참조 대상이 해제되면 잘못된 참조 발생 가능 |
사용 상황 | 참조 대상이 해제될 수 있고, nil이 허용될 때 | 참조 대상이 해제되지 않거나, 반드시 존재할 때 |
메모리 해제 여부 확인 필요 | 필요 (if let이나 guard let 사용) | 불필요 |
4. weak의 특징
- 약한 참조로, 참조 대상이 메모리에서 해제되면 자동으로 nil로 설정.
- 항상 옵셔널 타입이어야 하며, nil 가능성을 처리해야 함.
- 주로 delegate 패턴에서 사용.
class Person {
var name: String
weak var pet: Pet? // weak 참조
init(name: String) {
self.name = name
}
deinit {
print("\\(name) is being deinitialized.")
}
}
class Pet {
var name: String
var owner: Person? // 강한 참조
init(name: String) {
self.name = name
}
deinit {
print("\\(name) is being deinitialized.")
}
}
var john: Person? = Person(name: "John")
var fluffy: Pet? = Pet(name: "Fluffy")
john?.pet = fluffy // Person이 Pet을 weak 참조
fluffy?.owner = john // Pet이 Person을 강한 참조
john = nil // Person 해제
// "John is being deinitialized."
fluffy = nil // Pet 해제
// "Fluffy is being deinitialized."
- john?.pet은 weak 참조이므로, john이 해제되면 자동으로 nil로 설정.
5. unowned의 특징
- 비소유 참조로, 참조 대상이 메모리에서 해제되더라도 nil로 설정되지 않음.
- 옵셔널이 아닌 타입으로 선언.
- 참조 대상이 반드시 존재한다고 가정할 때 주로 사용.
- 참조 대상이 해제되면 런타임 오류가 발생.
class Customer {
var name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\\(name) is being deinitialized.")
}
}
class CreditCard {
var number: String
unowned let customer: Customer // unowned 참조
init(number: String, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Card \\(number) is being deinitialized.")
}
}
var john: Customer? = Customer(name: "John")
john?.card = CreditCard(number: "1234-5678-9012", customer: john!)
john = nil
// "John is being deinitialized."
// "Card 1234-5678-9012 is being deinitialized."
- CreditCard는 unowned 참조를 통해 Customer를 참조하며, john 해제 시 CreditCard도 정상적으로 해제.
6. weak와 unowned 사용 시 주의 사항
weak는 항상 옵셔널
- weak는 참조 대상이 해제되면 자동으로 nil로 설정되므로, 반드시 옵셔널 타입.
- 사용 시 if let이나 guard let로 안전하게 처리해야 함.
if let pet = person.pet {
print("\\(pet.name) is owned by \\(person.name).")
}
unowned는 옵셔널이 아님
- unowned는 참조 대상이 해제될 가능성이 없는 경우에만 사용해야 함.
- 참조 대상이 해제되면 런타임 오류가 발생하므로, 안전성에 주의.
강한 순환 참조 방지
- weak와 unowned는 강한 순환 참조를 방지하기 위해 설계.
- 참조 그래프를 설계할 때, 서로 참조하는 객체 간의 관계를 명확히 정의해야 함.
7. 언제 weak와 unowned를 사용할까?
상황 | weak 사용 | unowned 사용 |
참조 대상이 해제될 가능성이 있음 | ✅ | ❌ |
참조 대상이 반드시 존재한다고 가정됨 | ❌ | ✅ |
옵셔널 값 허용 | ✅ | ❌ |
delegate 패턴 | ✅ | ❌ |
순환 참조를 방지하고 안전성 필요 | ✅ | ❌ |
8. weak와 unowned의 실질적 사용 예
Delegate 패턴에서 weak
weak는 delegate 패턴에서 흔히 사용.
protocol TaskDelegate: AnyObject {
func taskDidComplete()
}
class Task {
weak var delegate: TaskDelegate?
func completeTask() {
delegate?.taskDidComplete()
}
}
class Manager: TaskDelegate {
func taskDidComplete() {
print("Task completed.")
}
}
let task = Task()
let manager = Manager()
task.delegate = manager
task.completeTask() // "Task completed."
부모-자식 관계에서 unowned
unowned는 부모-자식 관계에서 자식이 부모를 참조해야 할 때 사용.
class Parent {
var child: Child?
deinit {
print("Parent is being deinitialized.")
}
}
class Child {
unowned let parent: Parent
init(parent: Parent) {
self.parent = parent
}
deinit {
print("Child is being deinitialized.")
}
}
var parent: Parent? = Parent()
parent?.child = Child(parent: parent!)
parent = nil
// "Child is being deinitialized."
// "Parent is being deinitialized."
9. 결론
- weak: 참조 대상이 해제될 가능성이 있을 때 사용. 자동으로 nil로 설정되므로 안전함.
- unowned: 참조 대상이 반드시 존재한다고 가정할 때 사용. 메모리 해제 후 참조 시 런타임 오류 발생 가능.
- 강한 순환 참조 방지를 위해, 객체 간의 관계를 이해하고 weak와 unowned를 적절히 선택하는 것이 중요.
- 일반적으로 weak는 더 안전하지만, 성능이 중요하고 메모리 안전성이 보장되는 경우 unowned를 사용.
'Swift' 카테고리의 다른 글
[Swift] map (0) | 2024.04.19 |
---|---|
[Swift] init (0) | 2024.04.18 |
[Swift] self / Self (0) | 2024.04.18 |
[Swift] associatedtype (0) | 2024.04.18 |
[Swift] Protocol (0) | 2024.04.18 |