class Dog {
var name: String
var weight: Double
init(name: String, weight: Double) {
self.name = name
self.weight = weight
}
deinit {
print("\(name) 메모리 해제")
}
}
인스턴스를 하나씩 찍어내면 heap영역에 choco인스턴스 bori인스턴스가 올라감
→ 옵셔널 타입으로 설정한 이유는 nil을 할당할 수 있어야하기 때문
var choco: Dog? = Dog(name: "초코", weight: 15.0)
var bori: Dog? = Dog(name: "보리", weight: 10.0)
choco의 RC(레퍼런스 카운트)는 1이 되고 bori의 RC도 1이 된다
choco와 bori변수는 메모리 주소를 가지고 있는데 nil로 할당해주면 RC가 0이 된다
choco = nil // RC: 0
//release(choco)
bori = nil // RC: 0
//release(bori)
→ deinit이 호출된다
class Dog {
var name: String
var owner: Person?
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
class Person {
var name: String
var pet: Dog?
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
인스턴스를 각자만들면
이런 상황에서 bori변수와 gildong변수에 nil을 할당하더라도 RC가 1씩 줄어들어도
서로가 서로를 가리켜 메모리에서 해제되지 않는다
💡 객체가 서로를 참조하는 강한 참조 사이클로 인해변수의 참조에 nil을 할당해도 메모리 해제가 되지 않는
메모리 누수(Memory Leak)의 상황이 발생
weak var owner: Person?
weak var pet: Dog?
→ Person을 가리키지만 RC를 증가시키지 않음
→ 약한 참조의 경우, 참조하고 있던 인스턴스가 사라지면, nil로 초기화 되어있음
gildong = nil
bori?.owner // gildong만 메모리 해제시켰음에도 ===> nil
var num = 1
// 클로저를 변수에 담는 순간 클로저는 heap영역에 저장된다
// num이라는 변수를 캡처한다 -> 변수의 주소값을 가리키고 있다
let valueCaptureClosure = {
print("밸류값 출력(캡처): \(num)")
}
num = 7
valueCaptureClosure() // result : 밸류값 출력(캡처): 7
num = 1
valueCaptureClosure() // result : 밸류값 출력(캡처): 1
→ 만약에 캡쳐 리스트를 사용하게 되면 주소값이 아닌 값 자체를 복사해서 가지고 있다
// 이미 위에서 1이라는 num의 값을 복사해서 가지고 와 있는 상태
let valueCaptureListClosure = { [num] in
print("밸류값 출력(캡처리스트): \(num)")
}
// num에 7을 할당하더라도 num 1을 복사해서 가지고 있기 때문에 1을 출력
num = 7
valueCaptureListClosure() // 몇을 출력할까요? = 1
class SomeClass {
var num = 0
}
var x = SomeClass()
var y = SomeClass()
// x라는 변수를 가리킨다(RC를 증가시키지 않음)
print("참조 초기값(시작값):", x.num, y.num)
// 참조타입을 캡처리스트에 담는 순간 x자체를 가리키게된다(RC증가)
let refTypeCapture = { [x] in
print("참조 출력값(캡처리스트):", x.num, y.num)
}
var z = SomeClass()
let refTypeCapture1 = { [weak z] in
print("참조 출력값(캡처리스트):", z?.num)
}
class Dog {
var name = "초코"
func doSomething() {
DispatchQueue.global().async {
print("나의 이름은 \(self.name)입니다.")
}
}
}
→ ✅클로저 내에서 객체의 속성 및 메서드에 접근 시에는 "self키워드"를 ⭐️반드시⭐️ 사용해야함✅
💡 무조건 self를 붙여야하는데 방식이 두가지가 있다 1. self.name 2. [self] in name→ 비동기 처리를 진행할때 변수의 주소를 캡쳐하고 있으면 2번 쓰레드에서 독립적으로 작업을 할 수가 없게된다 그렇기 때문에 객체를 가리키고 있어야한다(강한참조가 필요)
class ViewController: UIViewController {
var name: String = "뷰컨"
func doSomething() {
DispatchQueue.global().async {
sleep(3)
print("글로벌큐에서 출력하기: \(self.name)")
}
}
deinit {
print("\(name) 메모리 해제")
}
}
func localScopeFunction() {
let vc = ViewController()
vc.doSomething()
}
localScopeFunction()
함수 실행의 결과 → 글로벌큐에서 출력하기, 뷰컨, 뷰컨 메모리 해제
weak self라면 → 뷰컨 메모리 해제, 글로벌큐에서 출력하기: nil