Swift

[Swift] 캡처리스트와 순환참조

양밀루 2025. 6. 5. 20:18

ARC 2탄 느낌으로 낋여본다

class로만 순환참조 예제를 보다가 클로저랑 캡처리스트에서 순환참조가 이해가 안되어 하는 정리!

 

✅ 클로저도 객체다

// Dog 예제
var dog1: Dog? = Dog(name: "멍멍이")  // 힙에 Dog 객체 생성
var dog2: Dog? = dog1                // 같은 객체 참조

// 클로저 예제 (동일한 원리!)
var completion: (() -> Void)?
let myClosure = { print("Hello") }   // 힙에 클로저 객체 생성
completion = myClosure               // 같은 클로저 객체 참조

-> 클로저도 힙에 생성되는 실제 객체이고, 변수들은 그 주소를 가리킨다!


✅ 캡처(Capture)란?

1. 캡처가 발생하는 정확한 순간

// ViewController.viewDidLoad() 내부
asyncFunction {
    self.label.text = "Complete"  // 이 순간 캡처!
}

2. 캡처의 본질

// 실제로 일어나는 일 (개념적)
// 원본 코드:
{ self.doSomething() }

// Swift가 내부적으로 생성하는 것:
class 자동생성된클로저 {
    let 캡처된self: ViewController  // 🚨 이것이 핵심!
    
    init(원본self: ViewController) {
        self.캡처된self = 원본self  // 참조 카운트 +1
    }
}

// 결국 이것과 동일:
let myViewController = self  // 참조 카운트 +1

-> 클로저는 사용하는 외부 변수를 "납치해서 내부 프로퍼티로 저장"


✅ 순환 참조의 발생 과정

1. 순환 참조가 만들어지는 과정

var completion: (() -> Void)?  // 전역 변수

func asyncFunction(_ completionHandler: @escaping () -> Void) {
    completion = completionHandler  // 같은 클로저 객체 참조
}

// ViewController에서:
asyncFunction {
    self.label.text = "Complete"  // self 캡처!
}

2. 메모리 관계도

ViewController → completion → 클로저객체 → self → ViewController
       ↑__________________________________________________|
                            순환 참조 완성! 🔄

 


✅ 해결책: weak self

1. 올바른 패턴

asyncFunction { [weak self] in
    guard let self = self else { return }
    self.label.text = "Complete"
}

2. 해결된 참조 관계

ViewController → completion → 클로저객체 → weak self (참조카운트 증가 안함)
                                                ↓
                                          ViewController

 

 

'Swift' 카테고리의 다른 글

[Swift] Result 타입  (1) 2025.06.17
[Swift] Any와 AnyObject  (0) 2025.06.12
[Swift] 고차함수 더 이상 헷갈리지 않기  (0) 2025.06.04
[WWDC] ARC in Swift  (0) 2025.05.30
Struct 접근제어와 Memberwise Initializers  (0) 2025.05.29