참조타입과 heap
인스턴스, 클로저 등등 참조타입은 자동으로 heap에 할당
힙에 할당?
Class Dog {
var name: String?
init(name: String?) }
self.name = name
}
}
let choco = Dog(name: "Choco")
실제 Dog 인스턴스는 힙에 할당되고,
지역변수 choco는 스택에 할당됨
스택에 있는 choco는 힙에 있는 인스턴스를 참조하고 있는 형태
즉, choco안에는 힙에 할당된 인스턴스의 주소값이 들어가 있음
힙영역의 인스턴스는 복사되지 않는다
let bori = choco
bori는 choco와 같은 힙 영역의 Dog 인스턴스를 가리키고 있음
힙은 사용하고 난 후 반드시 메모리 해제를 해줘야 한다
스택에 있는 choco와 bori가 함수 종료시점에 사라지면
힙에 남아있는 Dog 인스턴스는 누가 메모리 해제를 해줄까?
ARC가 해줌
ARC
클래스 인스턴스가 더 이상 필요하지 않을때 메모리를 자동으로 해제
그동안 힙의 메모리를 해제해주지 않아도 됐던 이유?
ARC가 자동으로 메모리 해제 해줬기 때문
즉, 힙에 할당된 인스턴스의 메모리를 알아서 관리해줌
참조계산 시점: 컴파일 시점에 언제 참조되고 해제되는지 결정되어 런타임 때 그대로 실행
장점: 개발자가 참조 해제시점을 파악할 수 있음
런타임 시점에 추가 리소스가 발생하지 않음
단점: 순환 참조가 발생시 영구적으로 메모리가 해제되지 않을 수있음
ARC의 메모리 관리 방법
메모리의 참조 횟수(Reference Conut)를 계산하여,
참조 횟수가 0이 되면
더이상 사용하지 않는 메모리라 생각해 해제
RC(참조횟수)?
인스턴스를 누가 가리키고 있냐 아니냐(참조하냐 안하냐)를 숫자로 나타내는것
(참조 횟수가 7이라면, 해당 인스턴스를 7군데에서 참조 하고 있다는 뜻
참조횟수가 0이라면 참조되지 않고있으니 필요없음
즉, 메모리 해제)
모든 인스턴스는 자신의 RC값을 가지고 있음
Reference Counting
➕ 참조횟수 +1
1️⃣ 인스턴스를 새로 생성할 때
2️⃣ 기존 인스턴스를 다른 변수에 대입할 때
➖ 참조횟수 -1
1️⃣ 인스턴스를 가리키던 변수가 메모리에서 해제됐을 때
func makeClone(_ origin: Dog) }
let clone = origin // 2. RC 2 makeClon함수가 실행되고 clone 변수가 생성되는 순간 RC+1
}
let choco = Dog(name: choco) // 1. RC 1 choco가 생성되는 순간 RC+1
makeDog(choco) // 3. RC 1 함수가 종료되고 지역변수 clone이 스택에서 해제되는 순간 RC-
2️⃣ nil이 지정됐을 때
3️⃣ 변수에 다른 값을 대입한 경우
var choco: Dog? = init(name: choco) // choco Instance RC 1
var clone: Dog? = init(name: cloneChoco)// clone Instance RC 1
choco = clone // choco Instance RC 0, clone Instance RC 2
4️⃣ 프로퍼티의 경우, 속해 있는 클래스 인스턴스가 메모리에서 해제될 때
class Contact {
var email: String?
init(email: String?) {
self.email = email
}
}
class Person {
var name: String?
var contact: Contact? = init(email: "hong@gmail.com")
init(name: String?) {
self.name = name
}
}
let hong: Person? = init(name: "hong")
hong = nil
hong이 가리키던 Person 인스턴스가 메모리에서 해제된다고해서,
프로퍼티인 contact가 가리키던 Contact 인스턴스 메모리도 같이 해제되는 것은 아님
Contact 인스턴스의 RC가 -1 될뿐!!!!