Swift의 Memory - 클로저와 캡처리스트

June·2023년 3월 17일
0

Swift

목록 보기
12/18
post-thumbnail

캡처리스트

클로저 내부에서 외부 변수들을 캡처할 때 strong, weak, unowned 등의 참조 강도를 명시해 캡처해오는 방식으로, 클로저 내부에서 캡처현상으로 인한 강한 참조 사이클 문제 해결을 위해 사용한다.

캡처리스트의 형태

// 1. 파라미터 없는 경우
{ [캡처리스트] in 
    print("print")
}

// 2. 파라미터 있는 경우
{ [캡처리스트](파라미터) -> 리턴형 in 
    print("print")
}



캡처리스트 사용 이유

1. 값 타입

외부 요인에 의한 값 변경 방지를 위해

var num = 1

let valueCaptureClosure = { [num] in    // 캡처리스트에서 value 타입 캡처
    print("밸류값 출력(캡처) : \(num)")
}

num = 7
valueCaptureClosure()    // 1

// 캡처리스트 사용시, 외부에 존재하는 num(밸류타입)의 값을 복사해서 사용
  • 밸류 타입의 캡처 현상은 클로저가 자신이 사용할 외부 변수를 캡처함으로서 발생한다. 밸류타입의 참조(메모리 주소)를 캡처해서 사용하게 된다는 뜻인데, 그렇게 되면 원본 값이 변하는 것이기 때문에 클로저 내부의 값도 변하게 된다.

  • 이 때 캡처리스트를 사용하면 클로저는 밸류타입의 메모리 주소를 캡처하는 것이 아닌, 밸류타입의 값을 '복사'해서 사용하게 된다.
    이렇게 외부 요인에 의해 값이 변경되는 것을 방지할 수 있다.

  • 밸류 타입 캡처에서는 상대방의 레퍼런스 카운팅을 올릴 수 없기 때문에, weak 혹은 unowned를 선언할 수도 없고 선언할 필요도 없다.


2. 참조 타입

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를 선언하는 것이다.

profile
안다고 착각하지 말기

0개의 댓글