클로저를 사용할 때 [weak self]를 선언해 주는 코드를 많이 본 적이 있습니다. weak는 순환참조를 해결하기 위해서 사용하는 것인데, 그럼 클로저를 사용할 때 순환참조의 위험이 있다는 것 같아서 그 부분을 알아보려고합니다.
클로저는 주변의 내용을 저장해두기위해 캡쳐가 일어나는데 이때 외부를 참조하면서 참조 카운트가 올라가서 순환 참조가 발생한다고합니다.
그럼 진짜인지 알아보기위해 코드를 작성해보겠습니다.
class Human {
let name: String
var onMissionComplete: (() -> Void)?
init(name: String) {
self.name = name
print("'\(name)' 생성됨 (init)")
}
func setupMission() {
onMissionComplete = {
print("'\(self.name)'의 미션이 완료되었습니다.")
}
}
}
deinit {
print("'\(name)' 소멸 (deinit)")
}
}
func checkClosureARC() {
print("--- 강한 참조 ---")
var human: Human? = Human(name: "민희")
human?.setupMission()
human = nil
print("-> Human을 nil로 변경\n")
}
위와 같이 작성해서 실행시키면 아래와 같은 결과가 출력됩니다.

Human을 nil로 변경하였지만 deinit이 호출되지 않았기 때문에 순환참조가 발생했다는 것을 알 수 있습니다.
그럼 순환참조를 해결하기위해 weak를 선언해보겠습니다.
class Human {
let name: String
var onMissionComplete: (() -> Void)?
init(name: String) {
self.name = name
print("'\(name)' 생성됨 (init)")
}
func setupWeakMission() {
onMissionComplete = { [weak self] in
guard let self = self else { return }
print("'\(self.name)'의 미션이 안전하게 완료되었습니다.")
}
}
deinit {
print("'\(name)' 소멸 (deinit)")
}
}
func checkClosureARC() {
print("--- 약한 참조 ---")
var weakHuman: Human? = Human(name: "김민희")
weakHuman?.setupWeakMission()
weakHuman = nil
print("-> weakHuman 참조를 nil로 변경")
}

weakHuman을 nil로 변경하자 deinit이 호출되는걸 확인할 수 있었습니다.
'iOS' 카테고리의 다른 글
| 클로저에서 순환참조 없애기 트러블 슈팅 (3) | 2025.08.21 |
|---|---|
| Swift의 비동기 프로그래밍 (GCD) (0) | 2025.08.20 |
| 함수 인자로 class를 받는다면 ARC의 변화가 있을까? (1) | 2025.08.18 |
| SwiftUI 프로퍼티 래퍼 (3) | 2025.08.14 |
| SwiftData (2) | 2025.08.13 |