ARC의 단점 중 하나는 순환 참조가 발생 시 영구적으로 메모리가 해제되지 않을 수 있다. 아니 근데 저기요 순환 참조가 뭐죠? 그걸 알아보러 가보자잉~
우리가 지금까지 했던 힙의 할당하는 방식이 강한 참조 방식이다!
즉 인스턴스의 주소값이 변수에 할당될 때, RC가 증가하면 강한 참조!
애초에 default 값이 strong~
하지만 strong에 문제는 바로 순.환.참.조
예시를 들어보자! 요즘 내가 킹받고 있는 꽃보다 남자로 예시를 들어보겠다. 먼저 준표랑 잔디를 등장시켜보겠다
class Man {
var name: String
var girlfriend: Woman?
init(name: String) {
self.name = name
}
deinit { print("맨킬뎃쉿스껄!") }
}
class Woman {
var name: String?
var boyfriend: Man?
init(name: String) {
self.name = name
}
deinit { print("우맨킬뎃쉿스껄!") }
}
var junpyo: Man? = .init(name: "준표")
var zandi: Woman? = .init(name: "잔디")
그리고 이 둘은 서로가 서로의 남친 여친임을 표시해준다.
메모리에서 볼 때 둘의 관계는 아래 그림과 같다.
junpyo?.girlfriend = zandi
zandi?.boyfriend = junpyo
그러면서 이 둘의 RC도 하나씩 증가했다.
자~ 이제 순환참조의 문제점에 대해 알아보자! 이 메모리의 주인이 나는 둘의 사이가 너무 킹받아서 죽여버리기로 결심을 했따. 물론 메모리 상에서만 ㅎㅎ
junpyo = nil
zandi = nil
이렇게 하면 나의 예상대로 메모리가 깔끔하게 사라져야 되는데 현실은 RC의 값이 둘다 1임으로 메모리 상에 살아서 아직도 나를 킹받게 하는 것이다...
즉 strong으로 선언된 변수들이 순환참조 됐을 시 큰 문제는
- 서로가 서로를 참조하고 있어서 RC가 0이 되지 못한다는 것이다!
- 또한 해당 인스턴스를 가리키던 변수도 nil로 지정했기 때문에 인스턴스 접근도 할 수 없어 메모리 해제도 못하는 최악의 상황이 펼쳐진다.
이것들을 해결하기 위해서 우리는 weak과 unowned를 알아야 한다.
weak 약한참조, 즉 인스턴스를 참조할 시, RC를 증가시키지 않는다.
- 참조하던 인스턴스가 메모리에서 해제된 경우, 자동으로 nil이 할당되어 메모리가 해제된다!
- weak은 프로퍼티를 선언한 이후, 나중에 nildㅣ 할당된다는 관점에서
무조건 옵셔널 타입의 변수- 둘 중에 수명이 더 짧은 인스턴스를 가리키는 애를 약한 참조로 선언함.
class Man {
var name: String
weak var girlfriend: Woman?
init(name: String) {
self.name = name
}
deinit { print("맨킬뎃쉿스껄!") }
}
class Woman {
var name: String?
var boyfriend: Man?
init(name: String) {
self.name = name
}
deinit { print("우맨킬뎃쉿스껄!") }
}
var junpyo: Man? = .init(name: "준표")
var zandi: Woman? = .init(name: "잔디")
junpyo?.girlfriend = zandi
zandi?.boyfriend = junpyo
여기서 준표랑 잔디를 죽이면
1) zandi, junpyo가 스택에서 가리키는 인스턴스 RC 둘다 1
2) 잔디의 RC가 0이 되면서 kill
3) 준표의 여자친구 속성이 0이 되면서 RC 0되면서 킬
4) 메모리 깨-끗>
weak과 비슷하게 RC값을 증가시키지 않아서 강한 순환 참조를 해결하지만,
인스턴스를 참조하는 도중에 해당 인스턴스가 메모리에서 사라질 일이 없다고 확신하는게 핵심임!
- 참조하던 인스턴스가 만약 메모리에서 해제된 경우에도 nil을 할당받지 못하고, 해제된 메모리 주소값을 계속 들고 있음
- 둘 중에 수명이 더 긴 인스턴스를 가리키는 애를 unowned 선언해야됨!
//week으로 선언될 경우
junpyo?.girlfriend / 결과값 : nil
//unowned으로 선언될 경우
junpyo?.girlfriend / 결과값 : error: Execution was interrupted