Live: 객체가 활성 상태이며, 참조 카운트가 초기화된 상태
Deiniting: 강한 참조 카운트가 0이 되어 deinit()이 호출되는 상태
Deinited: deinit()이 완료되었지만, 아직 unowned 참조가 존재하는 상태
Freed: 객체의 메모리가 해제되었지만, Side table이 아직 존재하는 상태
Dead: 모든 참조가 사라져 객체와 Side table이 완전히 해제된 상태
클래스 인스턴스의 메모리를 자동으로 관리해주는 시스템
인스턴스의 참조 횟수 추적을 통한 관리
class Car {
let model: String
let manufacturer: String
init(model: String, manufacturer: String) {
self.model = model
self.manufacturer = manufacturer
}
deinit {
print("The Car is about to be deallocated.")
}
}
var firstCar: Car? = Car(model: "EV6", manufacturer: "KIA")
var secondCar: Car? = firstCar
firstCar = nil
secondCar = nil
firstCar에 의해 참조 횟수 +1 (1)firstCar를 참조하는 secondCar 변수에 의해 참조 횟수 +1 (2)firstCar에 할당된 인스턴스를 nil로 교체 참조 횟수 -1 (1)secondCar에 할당된 인스턴스를 nil로 교체 참조 횟수 -1 (0)Car 객체를 ARC가 메모리에서 해제함.두 개 이상의 클래스 인스턴스가 서로를 강하게 참조하여 ARC가 이들을 메모리에서 해제하지 못하는 문제
class Person {
var dog: Dog?
}
class Dog {
var owner: Person?
}
var john: Person? = Person()
var fido: Dog? = Dog()
john?.dog = fido
fido?.owner = john
john = nil
fido = nil
Person, Dog 객체를 변수에 할당Person 객체의 프로퍼티 dog에 Dog 객체를 참조Dog 객체의 프로퍼티 owner에 Person 객체를 참조john 변수에 할당된 Person 객체를 해제fido 변수에 할당된 Dog 객체를 해제Person, Dog 객체의 참조 횟수가 0이 아니라서 ARC가 메모리에서 해제시키지 못하는 문제약한 참조로 참조 대상이 해제되면 자동으로 nil
참조 횟수를 증가 시키지 X
class Person {
weak var dog: Dog?
}
class Dog {
weak var owner: Person?
}
var john: Person? = Person()
var fido: Dog? = Dog()
john?.dog = fido
fido?.owner = john
john = nil
fido = nil
Person, Dog 객체를 변수에 할당Person 객체의 프로퍼티 dog에 Dog 객체를 약한 참조 (참조 횟수를 증가 시키지 않음)Dog 객체의 프로퍼티 owner에 Person 객체를 약한 참조 (참조 횟수를 증가 시키지 않음)john 변수에 할당된 Person 객체를 해제fido 변수에 할당된 Dog 객체를 해제⚠️ weak 키워드로 선언된 변수, 상수는 참조하던 객체가 nil이 되면 nil이 되어야 하기 때문에 Optional로 선언되어야 한다.
비소유 참조로 참조 대상이 해제되더라도 nil이 되지 않음.
참조 대상이 항상 존재한다고 확신할 수 있는 경우에 사용
class Person {
var dog: Dog?
}
class Dog {
unowned var owner: Person
}
var john: Person? = Person()
var fido: Dog? = Dog()
john?.dog = fido
fido?.owner = john
fido = nil
john = nil
Dog 객체가 소유하는 Person 객체가 Dog 객체가 살아있는 동안 항상 존재한다는 가정 하에 사용!
| 참조 방식 | 메모리 접근 방식 | 옵셔널 처리 | 성능 | 안전성 |
|---|---|---|---|---|
weak | 사이드 테이블을 통한 간접 접근 | 옵셔널 언래핑 필요 | 낮음 | 높음 |
unowned | 직접 메모리 접근 | 언래핑 불필요 | 높음 | 낮음 (런타임 크래시 위험) |
weak 참조는 다른 참조와 다르게 sideTable이란 곳에서 모든 객체의 RC 관리하기 때문에 추가적으로 시스템이 sideTable에 접근해 참조를 추적하고 변수를 nil로 바꿔주는 추가적인 메모리 관리로 성능에 영향 가능성
개발자 입장에서 Optional 변수를 사용하기 위해 옵셔널 언래핑으로 인한 조건문 처리로 성능에 영향 가능성
캡처 리스트는 주변의 변수나 인스턴스를 캡처함으로써 해당 객체의 참조 횟수를 증가시켜 순환 참조가 발생할 수 있다.
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { [weak self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
캡처 리스트를 사용해서 주변 변수, 인스턴스를 캡처할 때, [weak self], [unowned self] 와 같이 참조를 관리하는 방식을 정하여 순환 참조 방지
| 특성 | Java GC | Swift ARC |
|---|---|---|
| 메모리 해제 시점 | 예측 불가능 (GC 실행 시점에 따라 다름) | 예측 가능 (참조 횟수가 0이 되면 즉시 해제) |
| 런타임 오버헤드 | 주기적인 GC 실행으로 인한 오버헤드 발생 가능 | 낮음 (참조 횟수 관리로 인한 오버헤드 적음) |
| 순환 참조 처리 | GC가 자동으로 처리 | 개발자가 weak 또는 unowned로 관리 필요 |
| 실시간 처리 적합성 | 낮음 (GC로 인한 일시적인 지연 가능성) | 높음 (즉시 메모리 해제로 지연 최소화) |
가장 큰 특징은 GC의 경우, 주기적으로 GC가 동작하여 메모리를 관리하기 때문에 GC가 동작하는 시점에서 일시적으로 시스템이 느려질 수 있다.
다만 GC는 알아서 순환 참조를 해결해주기 때문에 개발자가 ARC에 비해 신경을 덜 써도 된다.
참고
https://codingmon.tistory.com/83