Swift 에서는 앱의 메모리를 사용을 관리하기 위해 ARC(Automatic Reference Counting)을 사용합니다.
존재 이유 : 메모리를 효율적으로 관리해주는 역할로써 참조되지않는 클래스 인스턴스를 메모리에서 해제함.
- 자동으로 참조횟수를 추적하여 관리함
- 더 이상 사용하지 않는 인스턴스를 메모리에서 해지함
- 하지만 몇몇의 경우 ARC메모리 관리를 위해 코드의 특정부분에 대한 관계에 대한 정보를 필요로 함( 참조 횟수 - class의 인스턴스에만 적용됨, 열거, 구조는 값타입임으로 해당사항이아님 )
ARC의 동작은 ??
- 클래스의 새 인스턴스를 만들때마다 ARC가 인스턴스 정보를 담는데 필요한 적정크기의 메모리를 할당함.
이 메모리는 그 인스턴스에 대한 정보와 관련된 저장 프로퍼티 값도 갖고 있는다. 추가적으로 인스턴스가 더이상 사용되지않을때 ARC가 그 인스턴스가 차지하고 있는 메모리를 해지해서 다른 용도로 사용할 수 있도록 확보해 둠
IF,,
만약에 ARC가 아직 사용중인 인스턴스를 메모리에 내렸는데 인스턴스의 프로퍼티에 접근한다면 앱은 크래시가 발생한다.
고로 ARC에서는 아직 사용중인 인스턴스를 해지하지 않기 위해 얼마나 많은 프로퍼티 상수 혹은 변수가 그 인스턴스에 대한 참조를 갖고 있는 지를 추적한다. 그래서 ARC는 최소 하나라도 그 인스턴스에 대한 참조가 있는 경우 그 인스턴스를 메모리에서 해지 하지 않습니다.
# 크래시(충돌) = 런타임오류
=> 쉽게말해 ARC를 메모리를 효율적으로 관리하기위해 안쓰는 인스턴스를 다운시키는데 혹여나 사용중인 인스턴스의 메모리를 해지할 수 있기때문에 그렇게 하지 않기 위해 얼마나 많은 프로퍼티와 상수 혹은 변수가 그 인스턴스에 대한 참조를 갖고 있는지를 추적한다.
그래서 최소 하나라도 그 인스턴스에 대한 참조가 있는 경우 그 인스턴스를 메모리에서 해지하지 않는다.
더 축약=> 참조횟수를 추적하여 1라도있으면 노 없으면 메모리 해지를 한다.
# 예시코드로
Class Kaui{
let name: String
init(name: String){
self.name = name
print(“\(name) is being initialized”)
}
deint {
print(“\(name) is being deintailized”)
}
Var reference1: Kaui?
Var reference2: Kaui?
Var reference3: Kaui?
Reference1 = Kaui(name: “Lim ka ui”)
Reference2 = Reference1
Reference3 = Reference1
2,3 모두 1번의 lim인스턴스를 참조하여 총 인스턴스를 3번 참조한것
고로 중복이 되었기때문에 마지막 3 제외하곤 메모리해지를 함
reference3 = nil
// Prints "John Appleseed is being deinitialized"
나머지 3까지 nil값을 처리한다면 메모리에서 해지되는것
# 클래스 인스턴스간 강한 참조 순환 (Strong Reference Cycles Between Class Instances) - 메모리 해지를 않되게 하는방법 ?
Class Kaui{
var gildong: Kaui = Kaui()
init(name: String){
self.name = name
print(“\(name) is being initialized”)
}
deint {
print(“\(name) is being deintailized”)
}
Var reference1: Kaui? // 첫 인스턴스 참조
Var reference2: Kaui? // 첫 인스턴스 참조
reference1.gildong = reference2 // 클래스 내부 변수로 두번째 인스턴스 참조
reference2.gildong = reference1 // 클래스 내부 변수로 두번째 인스턴스 참조
// 총 각각 2번씩 참조함
위에서 var reference1와 var reference2라는 변수들로 Kaui를 참조하고
클래스 내부에 있는 변수를 통해 한번더 참조해준다
기본적으로 이렇게 클래스 인스턴스를 참조하는 것을 "강한 참조"라고 칭한다.
만약에 여기서 reference1와 reference2에 nil값을 할당하면
reference1 = nil
reference2 = nil
reference1 변수가 Kaui를 참조하고 reference2변수가 Kaui를 참조했던게 메모리에서 해지가 된다
고로 각 reference1,2인스턴스 내부에 있는 gildong변수들을 통해 서로 참조하고있는 것들만남는것이다.
각 참조 횟수가 1번 으로 줄어 든다. 그래서 A,B값들이 메모리에서 해지되지않고 남아있지만 사용하지않더라도
ARC입장에서는 참조가 되고 있어 사용하는 줄 알고 있는 것이다. 이렇듯 메모리 누수가 발생 한다.
# 메모리 누수가 = 인스턴스가 해재되지않고 계속 남아있어 좋지않음
클래스 인스턴스간 강한 참조 순환 문제의 해결 (Resolving Strong Reference Cycles Between Class Instances)
2가지가 존재한다.
- weak, 약한참조
- unowned, 미소유참조
- 둘의 공통점
- 모두 ARC에서 참조 횟수를 증가시키지 않고 인스턴스를 참조합니다. 원조
- 참조의 값이 nil이되면 weak, unowned들의 인스턴스값도 메모리에서 해제됨
=> 이전에 강한 순환이 발생하여 메모리 공간이 차는걸 방지하기위해
- 둘의 차이점
- Weak는 옵셔널로 사용하고 unowned는 옵셔널을 사용하지않는다.
[약한참조]
예시
Class Kaui{
Weak var gildong: Kaui = Kaui()
init(name: String){
self.name = name
print(“\(name) is being initialized”)
}
deint {
print(“\(name) is being deintailized”)
}
Var reference1: Kaui? // 첫 인스턴스 참조
Var reference2: Kaui? // 첫 인스턴스 참조
reference1.gildong = reference2 // 클래스 내부 변수로 두번째 인스턴스 참조
reference2.gildong = reference1 // 클래스 내부 변수로 두번째 인스턴스 참조
// 총 각각 2번씩 참조함
reference1 = nil
reference2 = nil // nil값
강.참(강한참조) 사용시에는 Kaui참조와 클래스내부 gildong변수 참조까지 각 2개를 참조한상태였다
여기서 nil값을 부여하면 Kaui 클래스를 참조하는 값만 메모리에서 해지되고 reference1,2 각
내부 gildong인스턴스만 메모리에서 존재하였다.
하지만,,
전과 똑같은 코드조건에서 Weak 약.참(약한참조)을 사용하게되면
남는 메모리는 약한참조이기때문에 없다는 것이다.
unowned도 똑같이 사용이 되며 weak와 메모리 참조방식도 같다.
IF..
만약에 여기서 두개의 클래스로 나눠서 설계하고 하나의 인스턴스 참조만 약.참을 하게되면 어떻게 될까 ????
Class Kaui{
var gildong: Kaui2 = Kaui2()
init(name: String){
self.name = name
print(“\(name) is being initialized”)
}
deint {
print(“\(name) is being deintailized”)
}
Class Kaui2{
Weak var gildong: Kaui = Kaui()
init(name: String){
self.name = name
print(“\(name) is being initialized”)
}
deint {
print(“\(name) is being deintailized”)
}
Var reference1: Kaui? // 첫 인스턴스 참조
Var reference2: Kaui? // 첫 인스턴스 참조
reference1.gildong = reference2 // 클래스 내부 변수로 두번째 인스턴스 참조
reference2.gildong = reference1 // 클래스 내부 변수로 두번째 인스턴스 참조
// 총 각각 2번씩 참조함
reference1 = nil
reference2 = nil // nil값
대부분 약한참조를 사용하는 것이 안전하지만 원조 인스턴스의의 메모리 해지 후 접근을 하지않을 거라는 확신이 있을 경우 unowned을 사용한다.