캡처현상이란 클로저는 힙의 영역에 존재하고 변수에 할당 혹은 클로저를 호출하는 순간, 지속적으로 외부 변수를 사용해야 하기 때문에 외부 변수를 캡처하는 현상을 의미한다.
- 클로저 레퍼런스 타입
func calculateFunc() -> ((Int) -> Int) {
var sum = 0
func square(num: Int) -> Int {
sum += (num * num)
return sum
}
return square
}
var squareFunc = calculateFunc()
squareFunc(10) // 100
squareFunc(20) // 500
squareFunc(30) // 1400
캡처 현상을 해결하는 방법은 바로 캡처리스트
클로저가 객체의 변수로 선언된 경우, 클로저가 실행중인 동안 오래동안 객체를 사용해야하므로, 힙 영역에 저장하고 사용할 객체의 주소를 보관
→ self와 클로저 서로 참조하는 형태
self 가 존재할 때까지 접근할 수 없으므로 기본 클로저 내에서 self 를 참조할 수 있다는 의미class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
let heading = HTMLElement(name: "h1")
let defaultText = "some default text"
heading.asHTML = {
return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
}
print(heading.asHTML())
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
asHTML 프로퍼티는 클로저에 대해 강한 참조를 유지self.name 과 self.text 를 참조하는 방법 처럼 self 를 참조하므로 self 캡처 → 클로저가 HTMLElement 인스턴스에 강한 참조 유지
paragraph = nil
따라서 위와 같이 paragraph에 nil이 할당되도 클로저가 인스턴스에 대한 참조를 유지하고 있으므로 메모리에서 해제되지 않는다.
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}

paragraph = nil
// Prints "p is being deinitialized"
올바르게 메모리에서 해제된다.