참조 타입과 힙
class Country {
var name: String
var capital: String
init(name: String, capital: String) {
self.name = name
self.capital = capital
}
}
let korea = Country(name: "Korea", capital: "Seoul")
// korea는 지역변수라고 가정
Country라는 클래스를 korea라는 인스턴스로 생성하고 초기화 했다.
위와 같이 메모리에 저장 된다. 지역 변수인 korea는 스택에 할당되고, Country 인스턴스는 힙에 할당 된다.
스택에 있는 korea는 힙 영역에 있는 인스턴스를 "참조"하고 있는 형태이다.
따라서 korea 안에는 힙에 할당 된 인스턴스의 주소 값이 들어가 있다.
✅ 이전글 참고
let copyKorea = korea
위와 같은 작업을 해줘도 인스턴스는 복사되지 않는다.
같은 힙 영역의 인스턴스를 가리키고 있기 때문에 copyKorea의 capital 값을 변경하면 korea의 capital값도 변경 된다.
힙의 메모리 해제
힙은 사용하고 난 후에는 반드시 메모리 해제를 해줘야 한다.
메모리 해제를 위한 방법으로는 release, free 등이 있지만, 우리는 값 타입을 사용하면서 메모리를 해제한 적이 없다.
why? 스위프트에서는 ARC가 자동으로 메모리를 해제해주기 때문.
ARC (Automatic Reference Counting)
Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage.
In most cases, this means that memory management “just works” in Swift, and you don’t need to think about memory management yourself.
대부분의 경우 메모리 관리가 스위프트에서는 "Just works"한다는 것을 의미하고, 메모리 관리에 대해 직접 생각할 필요가 없다.
ARC automatically frees up the memory used by class instances when those instances are no longer needed.
ARC는 클래스 인스턴스에서 사용하는 메모리를 자동으로 해제한다.
However, in a few cases ARC requires more information about the relationships between parts of your code in order to manage memory for you. This chapter describes those situations and shows how you enable ARC to manage all of your app’s memory. Using ARC in Swift is very similar to the approach described in Transitioning to ARC Release Notes for using ARC with Objective-C.
Reference counting applies only to instances of classes. Structures and enumerations are value types, not reference types, and aren’t stored and passed by reference.
ARC란, 힙에 할당 된 인스턴스의 메모리를 자동으로 관리해주는 것을 의미한다.
1️⃣ ARC의 메모리 관리 방법
ARC는 RC(Reference Count)를 이용하여 메모리를 관리한다.
✅ 메모리의 참조 횟수(RC)를 계산하여, RC가 0이면 더 이상 사용하지 않는 메모리라 생각하여 자동으로 해제한다.
✅ 따라서 모든 인스턴스는 자신의 RC 값을 가지고 있다.
예를들어, RC가 5라고 가정하면 해당 인스턴스가 5군데에서 참조되고 있다는 뜻이다.
✔️ 참조 횟수 Count Up
RC가 Count Up 되는 순간은 인스턴스의 주소값을 변수에 할당할 때이다.
- 인스턴스를 새로 생성할 때
- 기존 인스턴스를 다른 변수에 대입할 때
✔️ 참조 횟수 Count Down
- 인스턴스를 가리키던 변수가 메모리에서 해제되었을 때
func makeCopyInstance (_ instance: Country) {
let copyInstance = instance // 2️⃣ RC = 2
}
let korea = Country(name: "Korea", capital: "Seoul") // 1️⃣ RC = 1
makeCopyInstance(korea) // 3️⃣ RC = 1
1️⃣ Korea 인스턴스 생성
Count Up되는 경우 중 인스턴스를 새로 생성할때이므로 RC가 +1 된다.
2️⃣ makeCopyInstance 함수 실행
makeCopyInstance 함수가 실행되어 korea를 참조하는 copyKorea 변수가 생성되는 순간은 기존 인스턴스를 다른 변수에 대입하는 경우이므로 RC가 +1 된다.
3️⃣ makeCopyInstance 함수 종료
이전에 함수 호출 시 함수의 지역변수, 매개변수, 리턴 값 등이 스택 영역에 저장되고, 함수가 종료되면 저장된 메모리도 해제한다고 했다. makeCopyInstance 함수가 종료되었으므로 저장된 메모리가 해제 되었고, 이 경우 인스턴스를 가리키던 변수가 메모리에서 해제된 경우이기 때문에 RC는 -1이 된다.
- nil이 지정되었을 때
let france: Country? = .init(name: "France", capital: "Paris") // 1️⃣ RC = 1
var copyFrance = france // 2️⃣ RC = 2
copyFrance = nil // 3️⃣ RC = 1
france = nil // 4️⃣ RC = 0 (메모리 해제)
- 변수에 다른 값을 대입한 경우
var korea = Country(name: "Korea", capital: "Seoul") // 1️⃣ korea RC = 1
var copyKorea = Country(name: "Korea", capital: "Cheongju") // 2️⃣ copyKorea RC = 1
korea = copyKorea // 3️⃣ korea RC = 0 (메모리 해제), copyKorea RC = 2
1️⃣ korea 인스턴스 생성
Count Up되는 경우 중 인스턴스를 새로 생성할때이므로 RC가 +1 된다.
2️⃣ copyKorea 인스턴스 생성
Count Up되는 경우 중 인스턴스를 새로 생성할때이므로 RC가 +1 된다.
3️⃣ korea에 copyKorea 대입
Count Down되는 경우 중 변수에 다른 값을 대입한 경우이므로,
korea의 RC는 0이되어 메모리가 해제되고, copyKorea는 Count Up되는 경우 중 기존 인스턴스를 다른 변수에 대입할 때이므로
copyKorea의 RC는 +1 되어 2가 된다.
- 프로퍼티의 경우, 속해 있는 클래스 인스턴스가 메모리에서 해제될 때
class Person {
init() {
print("Person init")
}
deinit {
print("❗️Person deinit")
}
}
class Department {
var person: Person? = .init()
init() {
print("Department init")
}
deinit {
print("❗️Department deinit")
}
}
var p1: Department? = .init()
p1 = nil
1️⃣ Department 인스턴스 생성
Department라는 클래스 안에 person이라는 클래스 인스턴스 프로퍼티가 존재한다.
Department인스턴스인 p1을 생성하면 Person RC와 Department RC 두 인스턴스 모두 Count Up되는 경우 중 인스턴스를 새로 생성할때이므로 RC가 +1 된다.
2️⃣ Department 인스턴스를 가리키던 p1 변수에 nil 할당
p1 변수에 nil을 할당할 경우 Count Down 경우 중 nil이 지정되었을 때이므로 p1의 RC는 -1 된다.
p1의 RC가 0이므로 메모리에서 해제되고, 이때 Department 인스턴스가 가지고 있던 person이라는 프로퍼티도 같이 메모리에서 해제 되므로 person 프로퍼티가 가리키고 있던 Person 인스턴스의 RC도 1 감소한다.
❗️ p1이 가리키던 Department 인스턴스가 메모리에서 해제된다고 해서 person 프로퍼티가 가리키던 Person 인스턴스의 메모리도 같이 해제되는 것은 아니다. Person 인스턴스의 RC가 1 감소할 뿐이다.
위의 예제처럼 RC가 감소했는데 0이 되면, 그땐 Person 인스턴스도 메모리에서 해제되는 것이다.
Department 클래스의 person 프로퍼티에 의해서 Person의 RC가 먼저 +1 되고, Department 의 RC가 +1 된다.
p1에 nil을 대입할 경우 p1이 가리키던 Department 인스턴스의 RC가 0이 되어 먼저 메모리가 해제되고,
person 프로퍼티가 가리키던 Person 인스턴스도 RC가 0이되어 이어서 메모리가 해제된다.
Reference
https://babbab2.tistory.com/26
https://manasaprema04.medium.com/memory-management-in-swift-heap-stack-arc-6713ca8b70e1
'iOS > iOS' 카테고리의 다른 글
[iOS] 네트워크 통신 - URL, URLComponents (0) | 2023.08.12 |
---|---|
[iOS] 네트워크 통신 - URLSession 간단한 예제 (0) | 2023.08.10 |
[iOS] 네트워크 통신 - URLSession (LifeCycle, GET/POST) (0) | 2023.08.10 |
[iOS] 네트워크 통신 - URLSession (URLSessionConfiguration, URLSessionTask) (0) | 2023.08.10 |
[iOS] 1. 메모리 구조 (0) | 2023.08.03 |