저번 포스팅에서 순환 참조와 ARC에 대해서 알아보았다.
이제 이러한 순환 참조가 실제로 일어나는 예시에 대해 살펴보자

Clousuer

우리는 흔히 클로져 에 [weak self] 키워드를 쓰는 것을 알 수 있다!
이것을 왜 사용하는 지 알아보기 전에 "Closure Capture"라는 것에 대해 알 필요가 있다.

Closure Capture

Closure는 주변 Context로 부터 상수나 변수를 Capture할 수 있는데, 먼저 예제를 살펴보자!

func makeIncrementer(forIncrement amount: Int) {
    var runningTotal = 0
    
    let closure = {
        runningTotal += amount
        print(runningTotal)
    }
    closure()
    closure()
}

해당 예시에서, runningTotal은 Closure 내부에 선언하지 않았지만,
사용할 수 있다!
이를 runningTotal이라는 변수가 Closure에 의해 Capture되었다고 한다.

또한, Value Type의 변수든, Reference Type의 변수든
Closure는 Reference Type으로 값을 캡처한다.

func makeIncrementer(forIncrement amount: Int) {
    var runningTotal = 0
    
    let closure = {
        runningTotal += amount
        print(runningTotal)
    }
    closure() // 10
    closure() // 20
}


Value Type의 경우 해당 문제를 Capture List를 통해 해결할 수 있다.

Capture List

Capture List미리 Capture할 변수, 상수들을 명시적으로 표현하는 것이다.

func makeIncrementer(forIncrement amount: Int) {
    var runningTotal = 0
    
    let closure = { [runningTotal, amount] in
        print(runningTotal)
    }
    closure() // 0
    
    runningTotal = 10
    closure() // 0
    
}

Value 타입의 경우는, Value type으로 값을 capture하지만,
"상수" 로 값을 캡처하게 된다.

let closure = { [runningTotal, amount] in
    runningTotal += amount //Error!
    print(runningTotal)
}

따라서, 해당 경우에 에러가 나게 된다.


Reference타입의 경우는, Referecne Type으로 값을 Capture한다.
또한 강한 참조로 값을 참조한다.

이는 Reference Counting이 증가하며, Closure로 인해 원하는 시점에서 인스턴스가 메모리에서 해제가 안될 수 있다.
즉! 메모리 누수가 발생한다.

저번 포스팅에서 보았듯이, 이는 weak 이라는 키워드로 해결을 하였는데,
이러한 이유 때문에 [weak self]를 사용하는 것이다.

Reference

Closures

복사했습니다!