iOS는 Garbage Collection이 아닌 ARC를 사용하여 메모리를 관리
ARC는 컴파일 타임에 삽입된 코드로 객체의 참조 횟구(Reference Count)를 추적하고, 참조가 0이되면 메모리를 자동으로 해제
class Person {
let name: String
init(name: String) { self.name = name }
deinit { print("\(name) is being deinitialized") }
}
var person1: Person? = Person(name: "Alice")
person1 = nil // 참조가 사라지므로 메모리 해제
| 참조 타입 | 설명 | 사용 시기 및 예시 |
|---|---|---|
| strong | 기본 참조. 참조 카운트를 증가시킴 | 대부분의 경우 기본 사용 |
| weak | 참조는 하지만, 참조 카운트에 영향 X. 옵셔널이어야 함 (ClassType?) | 순환 참조 방지용 (특히 delegate) weak var delegate: SomeDelegate? |
| unowned | 참조는 하지만, 참조 카운트에 영향 X. nil이 되지 않음 | 강한 참조 순환은 피하면서, 참조 대상이 deinit되기 전 반드시 살아있는 것이 보장될 때 unowned let owner: Person |
class Customer {
var card: CreditCard?
}
class CreditCard {
unowned let customer: Customer
init(customer: Customer) {
self.customer = customer
}
}
두 객체가 서로를 강하게 참조(strong)하면, 참조 횟수가 0이 되지 않아 deinit이 호출되지 않음
-> 메모리 누수
ex)
class A {
var b: B?
}
class B {
var a: A?
}
var a: A? = A()
var b: B? = B()
a?.b = b
b?.a = a
a = nil
b = nil // 순환 참조로 인해 해제되지 않음
ex) 클로저에서의 순환 참조
class ViewModel {
var name = "ViewModel"
lazy var printName: () -> Void = {
print(self.name) // self가 클로저를 소유, 클로저가 self를 참조 → 순환 참조
}
}
해결 방법
lazy var printName: () -> Void = { [weak self] in
print(self?.name ?? "nil")
}
| 항목 | ARC | Garbage Collection |
|---|---|---|
| 방식 | 참조 카운트 기반 (컴파일 시점) | 루트 객체 탐색 기반 (런타임 시점) |
| 메모리 해제 시점 | 참조가 0이 되는 즉시 | GC가 동작하는 타이밍에 일괄 처리 |
| 성능 | 예측 가능 (낮은 지연시간) | 불규칙한 지연 가능성 있음 (Stop-the-world) |
| 플랫폼 예시 | iOS (Swift, Objective-C) | Java, C#, Android 등 |
ARC의 장점
ARC의 단점
GC의 장점
GC의 단점