가장 단순한 메모리 관리 방법은 프로그램의 요청이 있을 때만 메모리를 할당하고, 필요하지 않을때 할당을 해제하는 것이다.
이렇게 메모리를 관리하는 이유는, 멀티 프로그래밍 환경에서 한정된 메모리를 더 효율적으로 이용할 수 있도록 하기 위함이다.
Swift는 ARC(Automatic Reference Counting)
를 통해 메모리를 관리한다.
Automatic Reference Counting 자동 참조 카운팅
nil
을 할당할 때마다 참조 횟수가 -1이 되는데 최종적으로 참조 횟수가 0이 되면 메모리에서 해제된다.참조의 기본은 strong(강한)참조다.
근데, 강한 참조 시 영구적으로 메모리가 해제되지 않는 순환 참조
가 발생될 수 있다.
아래와 같이 두 클래스가 존재한다고 하자.
이때, 두 클래스의 프로퍼티가 서로 다른 클래스를 참조하고 있다.
class Student {
let name: String
var school: **School?**
init(name: String) {
self.name = name
}
}
class School {
let schoolName: String
var student: **Student?**
init(schoolName: String) {
self.schoolName = schoolName
}
}
순환 참조는 아래와 같은 상황에 발생하게 된다.
let firstStudent = Student(name: "Jee")
let appleUniversity = School(schoolName: "애플대학교")
firstStudent.school = appleUniversity
Student
의 school
프로퍼티에 School
인스턴스의 주소값을 할당하는 것이다.
이렇게 서로 다른 객체가 서로를 참조하고 있는 형태를 순환 참조
라고 한다.
앞서 말한대로 nil
을 할당하면 참조 횟수가 -1이 되어야 한다.
근데, 이렇게 서로를 참조하는 순환 참조가 발생하게 되면 메모리 해제가 되지않고 계속 메모리에 할당된 상태로 남아있게된다.
즉, memory leak(메모리 누수 현상)
이 계속 발생하게 된다.
weak
weak
키워드를 붙여 선언을 하게 되면, 인스턴스 참조 시 참조 횟수를 증가시키지 않는다.nil
이 할당되므로, 항상 변수 + 옵셔널 타입이어야 한다.unowned
신용카드에는 명의자가 꼭 있어야 하는 것 처럼, 반드시 존재해야하는 이유가 있을 때 사용한다.
class Person {
let name: String
// 카드를 소지할 수도, 소지하지 않을 수도 있기 때문에 옵셔널로 정의합니다.
// 또, 카드를 한 번 가진 후 잃어버리면 안 되기 때문에 강한참조를 해야 합니다.
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class CreditCard {
let number: UInt
unowned let owner: Person // 카드는 소유자가 분명히 존재해야 합니다.
init(number: UInt, owner: Person) {
self.number = number
self.owner = owner
}
deinit {
print("Card #\(number) is being deinitailized")
}
}
var jee: Person? = Person(name: "Jee") // Person 인스턴스의 참조 횟수: 1
if let person: Person = jee {
// CreditCard 인스턴스의 참조 횟수: 1
person.card = CreditCard(number: 1004, owner: person)
// Person 인스턴스의 참조 횟수: 1
}
jee = nil // Person 인스턴스의 참조 횟수: 0
// CreditCard 인스턴스의 참조 횟수: 0
// Jee is being deinitialized
// Card #1004 is being deinitialized
참고 문서
1. https://babbab2.tistory.com/27
2. 야곰 책