Swift 코딩 팁: DispatchQueue에서 [weak self] 생략해도 되는 상황

작성일 :

Swift 코딩 팁: DispatchQueue에서 [weak self] 생략해도 되는 상황

Swift에서는 비동기적으로 작업을 처리할 때 DispatchQueue를 자주 사용합니다. 이 과정에서 메모리 누수를 방지하기 위해 [weak self]를 사용해야 하는 경우가 많습니다. 그러나 항상 [weak self]가 필요한 것은 아니며, 이를 생략해도 되는 상황이 있습니다. 이번 글에서는 이러한 상황을 구체적으로 살펴보고, 코드 예시를 통해 더욱 명확히 이해할 수 있도록 돕겠습니다.

DispatchQueue와 [weak self]의 기본 개념

DispatchQueue는 비동기 작업을 처리할 때 유용하게 사용되는 Swift의 기능입니다. 비동기 작업을 실행할 때, 자주 사용되는 클로저(closure) 내에서 self를 참조하게 되는데, 이 경우 강한 참조(Strong Reference)로 인해 메모리 누수가 발생할 수 있습니다. 이러한 문제를 피하기 위해 우리는 보통 [weak self]를 사용하여 약한 참조(Weak Reference)를 생성하고, 이를 통해 메모리 누수를 방지합니다.

다음은 DispatchQueue에서 [weak self]를 사용하는 일반적인 예시입니다:

swift
DispatchQueue.global().async { [weak self] in
    guard let self = self else { return }
    self.someMethod()
}

이 예시에서 [weak self]를 사용하여 클로저 내에서 self의 약한 참조를 생성합니다. 만약 self가 nil이 되면, 클로저 내에서 self를 사용할 수 없게 되어 메모리 누수 문제를 방지할 수 있습니다.

[weak self] 생략 가능한 상황

위의 코드에서는 [weak self]를 사용하여 메모리 누수를 방지하지만, 모든 경우에 항상 [weak self]가 필요한 것은 아닙니다. 특정 상황에서는 [weak self]를 생략해도 무방합니다. 이러한 상황을 이해하기 위해 몇 가지 예시를 살펴보겠습니다.

즉시 반환되는 클로저의 경우

만약 클로저가 실행되자마자 반환되는 경우, [weak self]를 생략할 수 있습니다. 이 경우에는 클로저가 즉시 실행되기 때문에 self가 비동기 작업이 완료될 때까지 지속되지 않습니다.

swift
DispatchQueue.global().async {
    self.someMethod()
}

위의 코드에서는 클로저가 즉시 실행되고 곧바로 반환되기 때문에 [weak self]를 생략해도 메모리 누수 문제가 발생하지 않습니다.

특정 이벤트에 종속된 클로저의 경우

클로저가 특정 이벤트에 종속되어 있고, 해당 이벤트가 한 번만 발생한다면 [weak self]를 생략해도 됩니다. 예를 들어, 뷰 컨트롤러가 로드될 때 한 번만 실행되는 클로저의 경우 [weak self]는 필요하지 않을 수 있습니다.

swift
override func viewDidLoad() {
    super.viewDidLoad()
    DispatchQueue.global().async {
        self.someMethod()
    }
}

뷰 컨트롤러의 생명 주기 동안 한 번만 실행되는 경우, [weak self] 없이 self를 사용할 수 있습니다.

KVO(Key-Value Observing)나 Notification의 콜백에서

KVO나 Notification을 사용할 때 콜백 클로저 내에서 [weak self]를 생략할 수 있습니다. 그러나 이 경우에는 가능하면 명시적으로 [weak self]를 사용하여 유지 주기를 명확히 하는 것이 좋습니다.

swift
NotificationCenter.default.addObserver(forName: .someNotification, object: nil, queue: .main) { notification in
    self.handleNotification(notification)
}

위의 코드에서는 알림이 발생할 때마다 콜백이 실행되며, 해당 콜백이 실행될 때 self가 이미 메모리에서 해제되지 않았음을 보장할 수 있다면 [weak self]를 사용하지 않아도 됩니다.

[weak self] 비슷한 대안: [unowned self]

[weak self]를 사용할 때 항상 Optional 타입으로 처리해야 하기 때문에 코드가 다소 복잡해질 수 있습니다. 이와 유사하지만 약간 다른 대안으로 [unowned self]를 사용할 수 있습니다. Unowned 참조는 Optional을 사용하지 않고, self가 nil이 아님을 확신할 수 있을 때 사용합니다.

swift
DispatchQueue.global().async { [unowned self] in
    self.someMethod()
}

그러나 Unowned 참조를 사용할 때 self가 nil이 되는 시나리오가 발생하면 런타임 오류가 발생할 수 있으므로 주의가 필요합니다.

결론

결론적으로, DispatchQueue에서 항상 [weak self]를 사용해야 하는 것은 아니며, 특정 상황에서는 이를 생략해도 무방합니다. 클로저가 즉시 실행되거나 한 번만 실행되는 경우, 특정 이벤트에 종속되어 있는 경우 등이 이에 해당합니다. 그러나 이러한 상황에서조차 명확한 코드 관리를 위해 [weak self]를 사용하는 것이 더 나을 수 있습니다. 각자의 상황에 맞게 적절히 사용하는 것이 중요합니다.