클로저 내부에서 외부 변수들을 캡처할 때 strong, weak, unowned 등의 참조 강도를 명시해 캡처해오는 방식으로, 클로저 내부에서 캡처현상으로 인한 강한 참조 사이클 문제 해결을 위해 사용한다.
// 1. 파라미터 없는 경우
{ [캡처리스트] in
print("print")
}
// 2. 파라미터 있는 경우
{ [캡처리스트](파라미터) -> 리턴형 in
print("print")
}
외부 요인에 의한 값 변경 방지를 위해
var num = 1
let valueCaptureClosure = { [num] in // 캡처리스트에서 value 타입 캡처
print("밸류값 출력(캡처) : \(num)")
}
num = 7
valueCaptureClosure() // 1
// 캡처리스트 사용시, 외부에 존재하는 num(밸류타입)의 값을 복사해서 사용
밸류 타입의 캡처 현상은 클로저가 자신이 사용할 외부 변수를 캡처함으로서 발생한다. 밸류타입의 참조(메모리 주소)를 캡처해서 사용하게 된다는 뜻인데, 그렇게 되면 원본 값이 변하는 것이기 때문에 클로저 내부의 값도 변하게 된다.
이 때 캡처리스트를 사용하면 클로저는 밸류타입의 메모리 주소를 캡처하는 것이 아닌, 밸류타입의 값을 '복사'해서 사용하게 된다.
이렇게 외부 요인에 의해 값이 변경되는 것을 방지할 수 있다.
밸류 타입 캡처에서는 상대방의 레퍼런스 카운팅을 올릴 수 없기 때문에, weak 혹은 unowned를 선언할 수도 없고 선언할 필요도 없다.
retain cycle 문제 방지를 위해
/* 강한 참조 사이클 문제 해결 : 캡처리스트 + weak & unowned */
// 이 코드에서는 실제로 강한 참조 문제가 발생하지 않는다.
var z = SomeClass()
// 약한 참조
let refTypeCapture1 = { [weak z] in
print("참조 출력값(캡처리스트): ", z?.num)
// 약한 참조의 경우, 가리키는 인스턴스나 객체가 없어지면 반드시 nil 할당이 가능해야 하기 때문에 옵셔널 타입이어야 한다.
}
refTypeCapture1() // Optional(0)
let refTypeCapture2 = { [unowned z] in
print("참조 출력값(캡처리스트): ", z?.num)
}
refTypeCapture2() // 0
강한 참조에서는 가리키는 상대방의 레퍼런스 카운트를 올리기 때문에 강한 참조 사이클 문제가 발생할 가능성이 있다.
해결 방법으로는 캡처리스트 내부에서 weak 혹은 unowned를 선언하는 것이다.