ARC (Automatic Reference Counting)
- 자동으로 메모리를 관리해 주는 방식
- 대부분의 경우에 개발자는 메모리 관리에 신경을 쓸 필요가 없음
- ARC는 더 이상 필요하지 않은 클래스의 인스턴스를 메모리에서 해제하는 방식으로 동작
- 메모리 관리는 메모리 영역 중, 힙 영역과 관련
- 힙 영역은 참조(Reference) 형 타입인 클래스, 클로저 등이 보관
- 값(Value) 타입인 구조체, 열거형 등은 메모 리 관리 대상이 아님
- 힙 영역의 참조형 자료들이 프로그램 상에서 얼마나 참조되는지 숫자를 세어서, 메모리 가 자동으로 할당 및 제거하도록 관리하는 것
ARC 동작
- ARC는 클래스 인스턴스를 생성하였을 때, 메모리를 할당
- 클래스 인스턴스가 더이 상 필요하지 않을 때, ARC는 해당 메모리를 해제
- ARC는 잘못된 메모리 접근을 막기 위해 얼마나 많은 프로퍼티들이 각각의 클래스 인스턴스를 참조하고 있는지 숫자를 카운트
- Swift에서는 각 객체마다 참조 횟수를 가지고 있고, 이 개수를 통해 메모리가 해제되어야 하는지를 결정
- 참조 횟수가 0이 되는 순간까지 ARC는 객체를 메모리에서 해제하지 않음
강한 참조 순환 (Strong Reference Cycle)
- 두 개의 참조형 타입의 데이터가 서로 강한 인스턴스로 참조하고 있어서, 참조 개수가 0개가 되지 않는 상황을 의미
- 이들 인스턴스는 자신을 선언한 변수가 nil 이 되어도, 서로에 대한 강한 참조 때문에 메모리를 빠 져나가지 못하게 되고, 이로 인해 메모리 누수(Memory Cycle) 발생
- 강한 참조 순환 문제를 해결하기 위해서는 두 가지 방법이 있음
- 하나는 weak 참조, 다른 하나는 unowned 참조를 사용하는 것
- weak 참조, unowned 참조 모두 ARC에서 참조 횟수를 증가시키지 않고 인스턴스를 참조하여 강한 참조 순환 문제를 해결가능
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartment \(unit) is being deinitialized")
}
}
약한 참조 (Weak References)
- 참조하고 있는 인스턴스가 먼저 메모리에서 해제될 때 사용
- 강한 참조와 다르게 참조 횟수를 증가시키지 않음
- 변수의 선언 앞에 weak 키워드를 작성하여 사용하며, nil 이 할당될 수 있어야 하기 때문에 항상 옵셔널 타입
- ARC에서 약한 참조에 nil을 할당하면 프로퍼티 옵저버는 실행되지 않음
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class Apartment {
let unit: String
weak var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartment \(unit) is being deinitialized")
}
}
미소유 참조 (Unowned References)
- 참조하고 있는 인스턴스가 같은 시점 혹은 더 뒤에 해제될 때 사용
- 해당 인스턴스에 대하여 Reference Counting을 하지 말라고 선언하는 것
- 변수의 선언 앞에 unowned 키워드를 작성하여 사용하며, 옵셔널 타입 일 수도 있고, 아닐 수도 있지만 nil 일 경우에 런타임 에러가 발생할 확률이 큼
- 참조하는 동안 해당 인스턴스가 메모리에서 해제되지 않으리라는 확신이 있는 경우에만 사용하는 것을 권장
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Card #\(number) is being deinitialized")
}
}
클로저에서의 강한 참조 순환
- 클래스와 마찬가지로, 참조 타입인 클로저에서도 강한 참조 순환이 발생 가능
- 클로저 내부에서 self를 캡처(capture)해서 사용할 수 있기 때문에, self를 약한 참조 혹은 미소유 참조로 지정하면 강한 참조 순환 문제를 해결 가능
- capture: 자신이 정의되는 시점에 주변 환경으로부터 여러 상수나 변수에 대한 참조를 잡아내어 저장
lazy var someClosure: () -> String = { [weak self] in
// ..
}
반응형
'⌨️ Language > swift' 카테고리의 다른 글
[Swift] Escaping Closure (1) | 2023.05.20 |
---|---|
[Swift] 접근 제어 (Access Control) (0) | 2023.02.07 |
[Swift] 제네릭 (Generic) (0) | 2023.02.07 |
[Swift] 에러 처리 (0) | 2023.02.07 |
[Swift] 고차함수 (0) | 2023.02.06 |
댓글