[Swift 기초] 자동참조계수, 강한참조, 약한참조
2022. 4. 4. 15:27ㆍSwift 기초 정리
728x90
//공부 기록용 포스팅입니다. 틀린 부분이 있을 경우 댓글로 알려주시면 감사합니다! 😎
0. 메모리 관리
- heap 영역: 동적으로 메모리를 할당할 때 위치하는 영역, 필요없을 때 소거되는 영역
1. 자동 참조 계수
- JAVA: 가비지 콜렉터를 사용. 메모리 관리는 편리하나 가비지 콜렉터가 수시로 실행되어 시스템에 부하
- C, C++: 개발자가 할당한 동적 메모리를 직접 해제해야 함, 에러 가능성 높음
- 자동참조 관리 기법
- 프로그램의 메모리 사용을 자동 참조계수를 통해 추적, 관리
- 필요없는 클래스 인스턴스 메모리를 자동적으로 해제
- 클래스의 인스턴스에만 적용
1-1. ARC: Automatic Reference Counting
class Person {
var name: String
init(name: String){
self.name = name
print("\\(name) is being initialized")
}
deinit{
print("\\(name) is being deinitialized")
}
}
var reference1: Person?
var reference2: Person?
var reference3: Person?
//ARC가 Person 인스턴스 메모리 유지 시작 참조 +1
reference1 = Person(name: "ㄱㅅㄱ")
//ㄱㅅㄱ is being initialized
reference2 = reference1 //참조 +1
reference3 = reference1 //참조 +1 : 참조 카운터 3
reference1 = nil //참조 -1
reference2 = nil //참조 -1
reference3 = nil //참조 -1: Person의 인스턴스 참조가 모두 없어져 ARC가 person 인스턴스 메모리 할당 해제
//ㄱㅅㄱ is being deinitialized
2. 강한 참조 순환
- 인스턴스가 서로 각각 1개 이상의 참조가 순환해서 생겨 ARC가 각각의 인스턴스 메모리를 유지 시작
- deinit을 제대로 하지 않으면 영원히 강한 참조 순환을 하며 메모리를 차지한다.
class Person {
let name: String
var apartment: Apartment?
init(name: String){
self.name = name
}
deinit{
print("\\(name) is being deinitialized")
}
}
class Apartment{
let number: Int
var tenant: Person?
init(number: Int){
self.number = number
}
deinit{
print("Apartment \\(number) is being deinitialized")
}
}
var john: Person?
var number73: Apartment?
//Person과 Apartemnt가 1개 이상의 참조가 생겨 ARC가 각각의 인스턴스 메모리 유지 시작
john = Person(name: "Jhon Appleseed")
number73 = Apartment(number: 73)
//Person 인스턴스와 Apartment인스턴스가 서로를 강한 참조
john?.apartment = number73
number73?.tenant = john
//인스턴스를 참조하는 변수는 해제되었지만 내부의 강한 참조는 유지되고 있다.
//이렇게 해제하면 참조할 방법이 없고, 이러한 객체들은 해제되지 않고 남아서 메모리를 차지한다.
//2-1 참고
john = nil
number73 = nil
- deinit이 출력되지 않음: 인스턴스를 참조하는 변수는 해제되어도 내부의 강한 참조는 해제되지 않음 → 참조를 먼저 해제하기
2-1. 강한 참조 순환 할당 해제하기
var john: Person?
var number73: Apartment?
//Person과 Apartemnt가 1개 이상의 참조가 생겨 ARC가 각각의 인스턴스 메모리 유지 시작
john = Person(name: "Jhon Appleseed")
number73 = Apartment(number: 73)
//Person 인스턴스와 Apartment 인스턴스가 서로를 강한 참조
john?.apartment = number73
number73?.tenant = john
//참조를 먼저 해제하고 인스턴스를 해제하기
john!.apartment = nil
number73!.tenant = nil
john = nil
number73 = nil
//Apartment 73 is being deinitialized
//Jhon Appleseed is being deinitialized
3. 강한 참조 순환 방지
3-1. 약한 참조
- 약한 참조는 인스턴스가 다른 인스턴스를 참조할 때 참조 카운터를 유지하지 않고 참조하는 방식: 참조하던 인스턴스가 할당 해제되면 nil을 할당
- 레퍼런스를 가지고 동작하지만 인스턴스를 해제해야할 지 판단할 때는 사용하지 않는다.
- 참조를 하되 강한 참조가 없을 경우 인스턴스는 해제된다.
- 약한 참조는 참조할 인스턴스가 존재하지 않을 가능성이 있으므로 반드시 옵셔널 타입의 변수로 선언해야 함
- weak 키워드를 프로퍼티 앞에 사용
- weak var tenant: Person?
- 참조 해제 (john!.apartment = nil) 없이 사용할 수 있다.
class Person {
let name: String
var apartment: Apartment?
init(name: String){
self.name = name
}
deinit{
print("\\(name) is being deinitialized")
}
}
class Apartment{
let number: Int
weak var tenant: Person?
init(number: Int){
self.number = number
}
deinit{
print("Apartment \\(number) is being deinitialized")
}
}
var john: Person?
var number73: Apartment?
//Person과 Apartemnt가 1개 이상의 참조가 생겨 ARC가 각각의 인스턴스 메모리 유지 시작
john = Person(name: "Jhon Appleseed")
number73 = Apartment(number: 73)
//Person 인스턴스는 Apartemnt 인스턴스에 대해 강한 참조를 함
//Apartemnt 인스턴스가 Person인스턴스에 대해 약한 참조를 함
john?.apartment = number73
number73?.tenant = john
//참조 해제가 없음!
//john이 nil이 되면 Person 인스턴스에 대한 강한 참조가 없으므로(= 참조 카운터가 0이므로) ARC가 Person 인스턴스를 할당 해제 시킨다.
//Apartment 인스턴스에 대한 강한 참조가 없어지므로 약한참조인 Apartment 인스턴스도 할당해제된다.
john = nil
//Jhon Appleseed is being deinitialized
(출처: 창원대학교 박동규 교수의 Swift 언어 기초 강좌, 16강 – 자동참조계수, 강한참조, 약한참조)
3-2. 미소유 참조(unowned reference)
- 참조하는 인스턴스가 언제나 값을 가지고 있다고 간주
- 옵셔널이 아닌 프로퍼티가 강한 참조를 가지지 않게 하려면 사용
- 프로퍼티가 nil을 가지지 않는 것을 전제로 unowned reference 사용
- 반드시 인스턴스를 참조하는게 확실할 때만 사용
- unowned 키워드를 프로퍼티 앞에 사용
class Employee{
var name: String
var team: Team?
init(name: String){
print("Employee init")
self.name = name
self.team = nil
}
deinit{
print("Employee deinit")
}
}
class Team {
var name: String
//Team 클래스의 leader 프로퍼티가 미소유 참조
//Team 클래스는 항상 leader 프로퍼티를 가져야 한다.(옵셔널 아님)
unowned var leader: Employee
init(name: String, leader: Employee){
print("Team init")
self.name = name
self.leader = leader
}
deinit {
print("Team deinit")
}
}
var emp1: Employee? = Employee(name: "ㄱㅅㄱ")
var team1: Team? = Team(name: "ㄱㅅㄱ팀", leader: emp1!)
//Employee init
//Team init
emp1!.team = team1
//강한 참조는 아니므로 참조 사이클이 생기지 않는다.
team1 = nil
emp1 = nil
//Employee deinit
//Team deinit
참고하였습니다. 감사합니다.
https://www.inflearn.com/course/창원대학교-박동규-교수의-swift-기초-강좌/lecture/1902?tab=note&mm=null
728x90
'Swift 기초 정리' 카테고리의 다른 글
[Swift 기초] 프로토콜 (0) | 2022.04.05 |
---|---|
[Swift 기초] assert와 guard (0) | 2022.04.05 |
[Swift 기초] 타입캐스팅 (0) | 2022.04.04 |
[Swift 기초] 인스턴스의 생성과 소멸 (0) | 2022.04.02 |
[Swift 기초] 클래스와 상속 (0) | 2022.04.02 |