Menu
Menu
Posts List
  1. 📌 반대로, weak self가 필요한 경우는?
    1. ✅ 1. DispatchQueue.main.async 내부
    2. ✅ 2. 값 타입(Value Type, Struct) 내부에서 클로저 실행
    3. ✅ 3. UIView.animate 내부
    4. ✅ 4. Timer.scheduledTimer(withTimeInterval:)에서 repeats: false일 때
    5. ✅ 5. NotificationCenter.addObserver에서 selector를 사용하는 경우
    6. weak self 와 unowned self 의 차이점
  2. 1. weak self
    1. 특징:
    2. 사용 예시:
    3. 언제 사용해야 할까?
  3. 2. unowned self
    1. 특징:
    2. 사용 예시:
    3. 언제 사용해야 할까?
  4. 3. weak vs unowned 비교
  5. 4. 언제 weak self 또는 unowned self를 사용해야 할까?
  6. 5. 실전 예제: weak vs unowned 비교
    1. 정리

SELF

작업을 하다보면 self capture 해야하는 상황이 많이 나온다.
그동안은 많은 부분에서 정확히 동작을 모른 상태로, 무작정 방지하기위해 weak self를 사용했는데, 조금 더 목적성에 맞게 사용하도록 사용해야겠다ㅠ

📌 예제 (weak self 필요 없음)

1
2
3
DispatchQueue.main.async {
self.label.text = "Hello, World!"
}
  • DispatchQueue.main.async 내부 코드는 메인 큐에서 실행되므로 self가 안전함
  • self가 사라지기 전에 UI 업데이트가 실행됨

📌 반대로, weak self가 필요한 경우는?

👉 self가 캡처된 후, 실행되기 전에 self가 해제될 가능성이 있는 경우

특히 백그라운드 작업을 실행하는 DispatchQueue.global().async {} 내부에서는 weak self를 사용해야 함

📌 예제 (weak self 필요)

1
2
3
4
5
6
7
8
DispatchQueue.global().async { [weak self] in
guard let self = self else { return }
let data = self.loadHeavyData()

DispatchQueue.main.async {
self.updateUI(with: data)
}
}

✔️ DispatchQueue.global().async 내부에서는 self를 강하게 참조하면 백그라운드 작업이 완료될 때까지 self가 해제되지 않음 (메모리 누수 발생 가능)

✔️ weak self를 사용하여 self가 해제된 경우 클로저 실행을 방지


📌 Swift에서 weak self가 필요하지 않은 클로저

1. DispatchQueue.main.async 내부

이유: 메인 스레드에서 즉시 실행되므로 self가 유지됨

1
2
3
DispatchQueue.main.async {
self.view.backgroundColor = .red
}

2. 값 타입(Value Type, Struct) 내부에서 클로저 실행

이유: 구조체(Struct)는 참조 타입이 아니라 복사되므로 weak self가 필요 없음

1
2
3
4
5
6
7
8
9
struct Example {
var message = "Hello"

func doSomething() {
DispatchQueue.global().async {
print(self.message) // Struct는 참조 타입이 아니므로 weak self 불필요
}
}
}

✔️ struct 내부에서 self를 캡처해도 순환 참조(Retain Cycle)가 발생하지 않음


3. UIView.animate 내부

이유: 애니메이션 블록이 실행될 때 self가 자동으로 캡처되므로 메모리 누수 발생 가능성 없음

1
2
3
UIView.animate(withDuration: 0.5) {
self.view.alpha = 0.5
}

✔️ 애니메이션 블록이 실행되는 동안 self가 유지되므로 weak self가 필요 없음


4. Timer.scheduledTimer(withTimeInterval:)에서 repeats: false일 때

이유: 한 번만 실행되는 타이머는 self를 강하게 참조하지 않음

1
2
3
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { timer in
self.doSomething()
}

✔️ repeats: false이므로 한 번 실행 후 타이머가 해제되므로 메모리 누수 발생 가능성 없음

📌 하지만, repeats: true일 경우에는 self가 지속적으로 참조되므로 weak self가 필요함

1
2
3
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { [weak self] timer in
self?.doSomething()
}

5. NotificationCenter.addObserver에서 selector를 사용하는 경우

이유: selector 기반 옵저버는 self를 직접 참조하지 않음

1
2
3
4
5
6
7
8
9

NotificationCenter
.default
.addObserver(
self,
selector: #selector(doSomething),
name: .someNotification,
object: nil
)

✔️ selectorself를 캡처하지 않으므로 weak self 불필요

📌 하지만, 클로저 기반의 addObserverself를 캡처하므로 weak self 필요

1
2
3
4
5
NotificationCenter
.default
.addObserver(forName: .someNotification, object: nil, queue: .main) { [weak self] notification in
self?.doSomething()
}

weak selfunowned self 의 차이점

Swift에서 weak selfunowned self는 **메모리 순환 참조(Strong Reference Cycle)**를 방지하기 위해 사용되는 방법! 하지만 두 개념에는 중요한 차이점이 있다.

1. weak self

특징:

  • 선택적으로 사용할 수 있도록 한다.
  • 참조하는 인스턴스가 자동으로 nil이 될 수 있다.
  • ARC(Automatic Reference Counting)가 해당 인스턴스를 메모리에서 해제할 때, weak reference는 자동으로 nil로 변경된다.
  • *Optional 타입(self?)**으로 사용해야 한다.

사용 예시:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyClass {
var name: String = "Weak Example"

func doSomething() {
DispatchQueue.global().async { [weak self] in
guard let self = self else { return } // self가 nil이면 실행되지 않음
print(self.name)
}
}
}

var myObject: MyClass? = MyClass()
myObject?.doSomething()
myObject = nil // MyClass 인스턴스가 메모리에서 해제됨

언제 사용해야 할까?

  • 클로저가 self를 캡처하면서 메모리 누수를 방지하고 싶을 때.
  • self가 해제될 가능성이 있고, 안전하게 nil 체크를 해야 할 때.

2. unowned self

특징:

  • unownednil이 될 수 없는 참조이므로 Optional이 아님.
  • self가 해제되었음에도 접근하면 **Crash(런타임 에러)**가 발생함.
  • unownedweak과 다르게 nil을 허용하지 않고, 항상 값이 있어야 하는 경우에 사용됨.

사용 예시:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyClass {
var name: String = "Unowned Example"

func doSomething() {
DispatchQueue.global().async { [unowned self] in
print(self.name) // self가 메모리에서 해제되었으면 Crash 발생
}
}
}

var myObject: MyClass? = MyClass()
myObject?.doSomething()
myObject = nil // MyClass 인스턴스가 해제됨 -> 이후 self에 접근하면 Crash 발생 가능

언제 사용해야 할까?

  • 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ViewController: UIViewController {
var name: String = "ViewController"

override func viewDidLoad() {
super.viewDidLoad()

// weak self 예제
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
guard let self = self else { return }
print(self.name) // self가 해제되었다면 실행되지 않음
}

// unowned self 예제
let closure: () -> Void = { [unowned self] in
print(self.name) // self가 해제되었을 경우 Crash 발생 가능
}
closure()
}
}

정리

  • weak은 메모리 해제를 허용하지만, 안전한 방식으로 nil 체크를 한다.
  • unowned는 더 빠를 수 있지만, 해제된 객체를 접근하면 앱이 Crash 날 수 있다.