1. 참조 타입과 메모리 관리의 기초
- Swift에서의 메모리 관리
Swift는ARC(Automatic Reference Counting)
를 사용해 메모리를 관리합니다. 이는 객체의 참조 수를 추적하여 더 이상 사용되지 않는 객체를 자동으로 해제합니다. - 강한 참조(Strong Reference)
객체가 다른 객체를 참조할 때 기본적으로 강한 참조를 사용합니다. 강한 참조는 참조 카운트를 증가시키며, 이로 인해 객체가 메모리에서 해제되지 않게 합니다.
2. 순환 참조와 메모리 누수
- 순환 참조의 정의
두 객체가 서로를 강하게 참조하면, 두 객체는 서로의 참조 카운트를 증가시키며, 더 이상 사용되지 않더라도 ARC가 객체를 해제하지 못하는 상태가 됩니다. 이를 **순환 참조(Retain Cycle)**라고 합니다. - 순환 참조의 예제
class Person {
var pet: Pet?
deinit {
print("Person deinitialized")
}
}
class Pet {
var owner: Person?
deinit {
print("Pet deinitialized")
}
}
var person: Person? = Person()
var pet: Pet? = Pet()
person?.pet = pet
pet?.owner = person
person = nil // Person 객체가 메모리에서 해제되지 않음
pet = nil // Pet 객체도 메모리에서 해제되지 않음
3. weak self와 unowned self
순환 참조를 방지하기 위해 Swift에서는 weak
와 unowned
키워드를 제공합니다. 두 키워드는 강한 참조 대신 약한 참조를 사용하도록 설정합니다.
3.1 weak self
- 특징
- 참조 대상이 메모리에서 해제되면 자동으로
nil
이 됩니다. - 참조는 옵셔널(
Optional
)이므로 사용 시 반드시 언래핑해야 합니다.
- 참조 대상이 메모리에서 해제되면 자동으로
- 적용 예시
class ViewController {
var name = "Declan"
func fetchData() {
DispatchQueue.global().async { [weak self] in
guard let self = self else { return }
print(self.name)
}
}
}
3.2 unowned self
- 특징
- 참조 대상이 메모리에서 해제되었는데도 접근하면 런타임 에러가 발생합니다.
- 옵셔널이 아니므로 강제 언래핑 없이 사용 가능합니다.
- 참조 대상이 해제되지 않는다는 전제가 있을 때 사용해야 안전합니다.
- 적용 예시
class ViewController {
var name = "Declan"
lazy var printName: () -> Void = { [unowned self] in
print(self.name)
}
}
4. weak self와 unowned self의 선택 기준
- weak self 사용:
- 대상 객체의 수명이 불확실하거나, 메모리에서 해제될 가능성이 있을 때 사용합니다.
- 예: 네트워크 요청, 비동기 작업 등.
- unowned self 사용:
- 대상 객체가 반드시 수명이 동일하거나 더 긴 경우에 사용합니다.
- 예: 부모-자식 관계에서 자식이 부모를 참조할 때.
5. 강한 참조 방지 패턴
- [weak self] 패턴
강한 참조를 피하기 위해 자주 사용하는 패턴입니다.
closure { [weak self] in
guard let self = self else { return }
// self를 안전하게 사용
}
- [unowned self] 패턴
자주 사용되지는 않지만, 강력한 성능 요구 사항이나 수명이 보장될 때 유용합니다.
6. 순환 참조 해결을 위한 실전 팁
- 클로저 내부에서 self 참조를 강하게 가지지 않도록
[weak self]
나[unowned self]
를 적극적으로 사용하세요. - 클래스 간 강한 참조 대신 delegate를 사용할 때도 약한 참조를 적용하세요.
protocol Delegate: AnyObject {
func didSomething()
}
class A {
weak var delegate: Delegate?
}
- 뷰 계층에서는
@escaping
클로저를 다룰 때 특히 주의해야 합니다.