Swift | 캡쳐 현상은 언제 발생하나 ?

일어나 개발해야지·2024년 3월 14일

iOS 글쓰기모임

목록 보기
3/12

캡쳐 현상은 언제 발생하나 ?

calculateFunc는 클로저를 반환하는 함수이다.
sum은 함수내에서는 지역변수이지만,
return 값인 square 기준에서는 외부변수이다.
square 함수는 sum을 지속적으로 필요로하기 때문에 값을 저장해야하는데,
이렇게 자신이 참조하는 외부의 변수를 저장되는 것을 캡쳐현상이라고 한다.
함수를 포함하는 환경을 캡쳐한다고도 표현함

func calculateFunc() -> ((Int) -> Int) {
    
    var sum = 0
    
    func square(num: Int) -> Int {
        sum += (num * num)
        return sum
    }
	//값이 아닌 함수를 return
      return square
}

var squareFunc = calculateFunc()


squareFunc(10) //100
squareFunc(20) //500
squareFunc(30) //1400

그럼 발생하지 않을 때는 ?

함수내의 모든 인스턴스가 새로 생성될 때이다
아래 calculate 함수는 위의 calculateFunc과 비슷해 보이지만
return 값인 result는 square에서 계산된 값을 저장한다.
calculate를 호출하면 모든 인스턴스가 초기화 되기때문에
여기서의 sum의 지역변수로 그 값은 항상 0이 된다.
캡처현상 또한 발생하지 않는다.

func calculate(number: Int) -> Int {
    
    var sum = 0
    
    func square(num: Int) -> Int {
        sum += (num * num)
        return sum
    }
    // 함수를 실행한 다음 결과를 result에 저장해서 
    let result = square(num: number)
    // 결과 "값"을 return하는
    return result
}

calculate(number: 10) //100
calculate(number: 20) //400
calculate(number: 30) //900

캡처현상

요약하자면, 함수가 외부에 있는 변수를 필요로 하는 상태일때,
사용중인 변수가 사라지면 에러가 발생할 수 있으므로
값을 지속적으로 저장하는 현상이 발생하고 이를 캡처현상이라고 한다

캡처리스트

클로저에서 약한 참조, 비소유 참조로 선언할 수 있도록 참조 강도를 명시하는 자리

let refTypeCapture = { [캡쳐리스트] in
   print(x, y)
}

클로저와 캡처현상

클로저를 변수에 할당하거나 클로저를 호출할때 캡처현상이 발생하는데
그 대상이 값타입일때와 참조타입일때로 구분된다.

1. 값타입을 캡쳐할 때

var num =1 
캡쳐캡쳐리스트
코드let closure = {print(num)}let clousure = {[num] in print(num)}
설명외부에 있는 변수의 주소를 전달①저장방식을 변경 (메모리주소 → 복사된 값) ②복사된 값을 전달 ③캡쳐이후 업데이트 안됨

2. 참조타입을 캡쳐할 때

class SomeClass{var num = 0}
var x = SomeClass()
캡쳐캡쳐리스트
코드let refClosure = {print(x.num)}let refClosure = {[x] in print(x.num)}
설명클로저 외부에 존재하는 참조타입의 변수주소를 복사해서 사용클로저 외부에 존재하는 참조타입의 주소값을 복사해서 사용
X레퍼런스 카운팅2 (주의필요)

주의: 참조타입에서 캡쳐리스트를 사용할때 레퍼런트 카운팅이 해제되지 않을 수 있으니 주의가 필요하며, weak와 unoknowed를 사용해서 해결이 가능

강한 참조 사이클의 해결

클로저는 참조타입으로 힙에서 동작하기 때문에 대상의 메모리 주소를 저장하는 특징이 있다.
따라서 무조건 캡쳐현상이 발생하는데,
우리는 캡쳐리스트를 사용해서 이 부분을 조절 할 수 있다.
값타입은 값을 복사/캡쳐 (값 변경 방지)
참조타입은 weak / unowned 로 선언으로 레퍼런스 카운팅이 올라가는 것을 방지한다.

1개의 댓글

comment-user-thumbnail
2024년 3월 14일

1번 square는 함수를 반환하기 때문에 sum도 계속 살아있지만
2번 값타입을 반환하기 때문에 함수가 사라지고 sum도 사라진다고 볼 수 있겠군요

외부의 참조타입을 클로저 내에서 사용할 때 캡쳐리스트를 사용하지 않으면 레퍼런스 카운팅에는 어떤 효과가 생기나요?
표가 비어있길래 깜빡하신건가 싶어서요

var num =1 이거 아마 컴파일 오류 날거에요
스위프트는 할당 연산자 사이의 띄어쓰기 수가 동일해야 하니까요

답글 달기