작업을 하다보면 self capture 해야하는 상황이 많이 나온다.
그동안은 많은 부분에서 정확히 동작을 모른 상태로, 무작정 방지하기위해 weak self를 사용했는데, 조금 더 목적성에 맞게 사용하도록 사용해야겠다ㅠ
📌 예제 (weak self 필요 없음)
1 | DispatchQueue.main.async { |
DispatchQueue.main.async내부 코드는 메인 큐에서 실행되므로self가 안전함self가 사라지기 전에 UI 업데이트가 실행됨
📌 반대로, weak self가 필요한 경우는?
👉 self가 캡처된 후, 실행되기 전에 self가 해제될 가능성이 있는 경우
특히 백그라운드 작업을 실행하는 DispatchQueue.global().async {} 내부에서는 weak self를 사용해야 함
📌 예제 (weak self 필요)
1 | DispatchQueue.global().async { [weak self] in |
✔️ DispatchQueue.global().async 내부에서는 self를 강하게 참조하면 백그라운드 작업이 완료될 때까지 self가 해제되지 않음 (메모리 누수 발생 가능)
✔️ weak self를 사용하여 self가 해제된 경우 클로저 실행을 방지
📌 Swift에서 weak self가 필요하지 않은 클로저
✅ 1. DispatchQueue.main.async 내부
이유: 메인 스레드에서 즉시 실행되므로 self가 유지됨
1 | DispatchQueue.main.async { |
✅ 2. 값 타입(Value Type, Struct) 내부에서 클로저 실행
이유: 구조체(Struct)는 참조 타입이 아니라 복사되므로 weak self가 필요 없음
1 | struct Example { |
✔️ struct 내부에서 self를 캡처해도 순환 참조(Retain Cycle)가 발생하지 않음
✅ 3. UIView.animate 내부
이유: 애니메이션 블록이 실행될 때 self가 자동으로 캡처되므로 메모리 누수 발생 가능성 없음
1 | UIView.animate(withDuration: 0.5) { |
✔️ 애니메이션 블록이 실행되는 동안 self가 유지되므로 weak self가 필요 없음
✅ 4. Timer.scheduledTimer(withTimeInterval:)에서 repeats: false일 때
이유: 한 번만 실행되는 타이머는 self를 강하게 참조하지 않음
1 | Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { timer in |
✔️ repeats: false이므로 한 번 실행 후 타이머가 해제되므로 메모리 누수 발생 가능성 없음
📌 하지만, repeats: true일 경우에는 self가 지속적으로 참조되므로 weak self가 필요함
1 | Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { [weak self] timer in |
✅ 5. NotificationCenter.addObserver에서 selector를 사용하는 경우
이유: selector 기반 옵저버는 self를 직접 참조하지 않음
1 |
|
✔️ selector는 self를 캡처하지 않으므로 weak self 불필요
📌 하지만, 클로저 기반의 addObserver는 self를 캡처하므로 weak self 필요
1 | NotificationCenter |
weak self 와 unowned self 의 차이점
Swift에서 weak self와 unowned self는 **메모리 순환 참조(Strong Reference Cycle)**를 방지하기 위해 사용되는 방법! 하지만 두 개념에는 중요한 차이점이 있다.
1. weak self
특징:
- 선택적으로 사용할 수 있도록 한다.
- 참조하는 인스턴스가 자동으로 nil이 될 수 있다.
- ARC(Automatic Reference Counting)가 해당 인스턴스를 메모리에서 해제할 때, weak reference는 자동으로 nil로 변경된다.
- *Optional 타입(
self?)**으로 사용해야 한다.
사용 예시:
1 | class MyClass { |
언제 사용해야 할까?
- 클로저가 self를 캡처하면서 메모리 누수를 방지하고 싶을 때.
self가 해제될 가능성이 있고, 안전하게 nil 체크를 해야 할 때.
2. unowned self
특징:
unowned는 nil이 될 수 없는 참조이므로 Optional이 아님.self가 해제되었음에도 접근하면 **Crash(런타임 에러)**가 발생함.unowned는weak과 다르게 nil을 허용하지 않고, 항상 값이 있어야 하는 경우에 사용됨.
사용 예시:
1 | class MyClass { |
언제 사용해야 할까?
- self가 해당 클로저의 실행 동안 반드시 살아있을 때.
self가 해제되지 않음을 100% 확신할 때.- 보통 두 개의 객체가 서로를 참조할 때(
parent-child 관계등) 많이 사용됨.
3. weak vs unowned 비교
| weak | unowned | |
|---|---|---|
| 참조 타입 | Optional |
Non-Optional |
| nil이 될 수 있는가? | ✅ Yes (자동으로 nil) | ❌ No (해제되면 Crash) |
| 언제 사용? | self가 해제될 가능성이 있을 때 | self가 항상 존재할 것으로 보장될 때 |
| 실행 시 Crash 발생 가능성 | 없음 (nil 체크 필요) | 있음 (해제된 메모리 접근 시) |
4. 언제 weak self 또는 unowned self를 사용해야 할까?
self가 nil이 될 수 있는 경우:
→
weak self사용 (guard let self = self else { return }패턴과 함께)self가 반드시 존재해야 하는 경우:
→
unowned self사용 (하지만self가 해제되었을 경우 앱이 Crash 날 수 있음)
5. 실전 예제: weak vs unowned 비교
1 | class ViewController: UIViewController { |
정리
weak은 메모리 해제를 허용하지만, 안전한 방식으로nil체크를 한다.unowned는 더 빠를 수 있지만, 해제된 객체를 접근하면 앱이 Crash 날 수 있다.