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]
를 사용하는 일반적인 예시입니다:
swiftDispatchQueue.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
가 비동기 작업이 완료될 때까지 지속되지 않습니다.
swiftDispatchQueue.global().async { self.someMethod() }
위의 코드에서는 클로저가 즉시 실행되고 곧바로 반환되기 때문에 [weak self]
를 생략해도 메모리 누수 문제가 발생하지 않습니다.
특정 이벤트에 종속된 클로저의 경우
클로저가 특정 이벤트에 종속되어 있고, 해당 이벤트가 한 번만 발생한다면 [weak self]
를 생략해도 됩니다. 예를 들어, 뷰 컨트롤러가 로드될 때 한 번만 실행되는 클로저의 경우 [weak self]
는 필요하지 않을 수 있습니다.
swiftoverride func viewDidLoad() { super.viewDidLoad() DispatchQueue.global().async { self.someMethod() } }
뷰 컨트롤러의 생명 주기 동안 한 번만 실행되는 경우, [weak self]
없이 self
를 사용할 수 있습니다.
KVO(Key-Value Observing)나 Notification의 콜백에서
KVO나 Notification을 사용할 때 콜백 클로저 내에서 [weak self]
를 생략할 수 있습니다. 그러나 이 경우에는 가능하면 명시적으로 [weak self]
를 사용하여 유지 주기를 명확히 하는 것이 좋습니다.
swiftNotificationCenter.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이 아님을 확신할 수 있을 때 사용합니다.
swiftDispatchQueue.global().async { [unowned self] in self.someMethod() }
그러나 Unowned
참조를 사용할 때 self
가 nil이 되는 시나리오가 발생하면 런타임 오류가 발생할 수 있으므로 주의가 필요합니다.
결론
결론적으로, DispatchQueue
에서 항상 [weak self]
를 사용해야 하는 것은 아니며, 특정 상황에서는 이를 생략해도 무방합니다. 클로저가 즉시 실행되거나 한 번만 실행되는 경우, 특정 이벤트에 종속되어 있는 경우 등이 이에 해당합니다. 그러나 이러한 상황에서조차 명확한 코드 관리를 위해 [weak self]
를 사용하는 것이 더 나을 수 있습니다. 각자의 상황에 맞게 적절히 사용하는 것이 중요합니다.