클로저의 캡쳐 타입이 결정되는 기준

임혜정·2024년 6월 13일
0

클로저가 값을 캡처할 때 값 캡쳐(Value)와 참조 캡쳐(Reference)로 나뉜다. 이는 주로 값 타입과 참조 타입의 특성에 의해 결정된다. 예를 들어 클로저에 의해 캡쳐되는 것이 클래스라면 클래스는 참조 타입이므로 그 인스턴스에 대한 "참조"를 캡쳐한다.

클래스로 참조 캡쳐의 예시

class MyClass { 
    var value = 10
}

func captureReference() -> () -> Void {
    let instance = MyClass() // 1. 클래스이므로
    let closure = {
        print("Value is \(instance.value)")//2.⭐️참조 캡쳐
    }
    instance.value = 20 // 인스턴스의 상태 변화
    return closure
}

let myClosure = captureReference()
myClosure()  //3. 20이 출력된다

- 클로저가 클래스 인스턴스를 캡쳐했고 클래스는 참조타입이므로 인스턴스의 상태가 변할 때 클로저에서도 그 변화를 반영한다.


구조체와의 비교

struct MyStruct {
    var value = 10
}

func captureValue() -> () -> Void {
    var instance = MyStruct() //1.스트럭트
    let closure = {
        print("Value is \(instance.value)") //2.이므로 값 캡쳐
    }
    instance.value = 20
    return closure
}

let myClosure = captureValue()
myClosure() // 3.10이 출력된다.

클래스와 구조체라는 것 빼고는 같은 상황. 그러나 출력 값이 10인걸 볼 수 있다.
이는 클로저나 구조체나 열거형같은 값 타입을 캡쳐할 때 복사본을 캡쳐하기 때문이다. 따라서 캡쳐된 값은 변하지않고 원본이 변경되더라도 클로저 내의 값에는 영향을 미치지 않는다.


캡쳐 리스트

클로저의 강한 참조 순환을 방지하기 위해 캡쳐 리스트를 사용한다. 이를 통해 약한 참조(weak)나 미소유 참조(unowned)로 캡쳐할 수 있다.

class MyClass {
    var value = 10
}

func captureWithList() -> () -> Void {
    let instance = MyClass()
    let closure = { [weak instance] in 
        if let instance = instance {
            print("Value is \(instance.value)")// ⭐️
        } else {
            print("Instance is nil")
        }
    }
    return closure
}

let myClosure = captureWithList()
myClosure() // 출력: Value is 10 (또는 Instance is nil, 인스턴스의 생명 주기에 따라 다름)

3줄 요약

  • 참조 캡쳐는 클로저가 클래스 인스턴스를 캡쳐할 때. 인스턴스 상태변화가 클로저 내에서도 반영됨
  • 값 캡쳐는 클로저가 구조체, 열거형을 캡쳐할때. 클로저는 캡쳐 시점의 값을 유지함.
  • 캡쳐 리스트를 사용하여 참조 타입의 강한 참조 순환을 방지한다
profile
오늘 배운걸 까먹었을 미래의 나에게..⭐️

0개의 댓글