Swift의 closure에서 weak self를 사용해야 하는 경우
Swift의 클로저에서 'weak self'를 사용해야 하는 경우
Swift에서 클로저는 매우 강력한 기능을 제공하지만, 잘못 사용하면 메모리 누수(memory leak)와 같은 문제를 일으킬 수 있습니다. 특히 클로저가 객체의 참조를 강하게 유지할 때, 강한 참조 순환(strong reference cycle)이 발생할 수 있습니다. 이로 인해 메모리 관리 문제가 발생합니다. 이를 피하기 위해 'weak self' 키워드를 사용하게 됩니다. 이 글에서는 'weak self'를 사용해야 하는 경우와 그 이유에 대해 설명하겠습니다.
클로저와 강한 참조 순환
Swift에서 클로저는 참조 타입(reference type)이며, 객체의 프로퍼티로 사용될 때 그 객체를 강하게 참조할 수 있습니다. 예를 들어, 객체 내에서 클로저를 프로퍼티로 갖고, 그 클로저가 다시 그 객체를 참조한다면 강한 참조 순환이 발생할 수 있습니다. 아래 코드 예제를 통해 설명합니다:
swiftclass MyClass { var closure: (() -> Void)? func doSomething() { closure = { print("Doing something in MyClass") } } } let instance = MyClass() instance.doSomething() instance.closure?()
위 코드에서 instance
는 MyClass
의 인스턴스로, doSomething
메서드를 호출하면 closure
프로퍼티가 클로저를 할당받습니다. 이 클로저는 MyClass
인스턴스를 강하게 참조하여 메모리에서 해제되지 않는 문제가 발생할 수 있습니다.
weak self
키워드로 해결
이러한 강한 참조 순환을 피하기 위해 weak self
키워드를 사용할 수 있습니다. weak self
를 사용하면 클로저 내부에서 객체를 약한 참조(weak reference)로 유지할 수 있습니다. 약한 참조는 객체가 메모리에서 해제되는 것을 막지 않습니다. 따라서 클로저 내에서 객체가 강하게 참조되지 않습니다.
다음 예제를 통해 weak self
사용을 설명하겠습니다:
swiftclass MyClass { var closure: (() -> Void)? func doSomething() { closure = { [weak self] in guard let self = self else { return } print("Doing something in MyClass") } } } let instance = MyClass() instance.doSomething() instance.closure?()
이 예제에서 클로저는 [weak self]
캡처 리스트를 사용하여 self
를 약하게 참조합니다. 클로저 실행 시점에 self
가 존재하는지 확인하기 위해 guard let self = self else { return }
구문을 사용합니다. 이를 통해 강한 참조 순환을 방지할 수 있습니다.
언제 weak self
를 사용해야 할까?
다음은 weak self
를 사용해야 하는 경우입니다:
- 비동기 작업: 네트워크 요청, 타이머, 비동기 클로저 등의 작업에서
self
를 참조할 때weak self
를 사용하여 강한 참조 순환을 방지합니다. - 클로저 속 콜백: 콜백이 객체를 소유하거나 객체가 콜백을 소유할 때 약한 참조를 사용하여 두 객체가 서로를 강하게 참조하지 않도록 합니다.
- 중간 상태 유지: 클로저 내에서 객체의 중간 상태를 유지하는 경우에
weak self
를 사용하여 객체가 메모리에 계속 남아있지 않도록 합니다.
아래는 네트워크 요청 예제입니다:
swiftclass NetworkManager { var completionHandler: (() -> Void)? func fetchData() { completionHandler = { [weak self] in guard let self = self else { return } // 데이터 처리 코드 print("Fetched data") } } } let networkManager = NetworkManager() networkManager.fetchData() networkManager.completionHandler?()
네트워크 요청이 완료되기 전 NetworkManager
인스턴스가 해제되더라도 클로저 내에서의 self
참조가 약한 참조인 경우 메모리 누수를 방지할 수 있습니다.
결론
Swift에서 클로저를 사용할 때 weak self
를 사용하면 강한 참조 순환을 방지하고 메모리 관리 문제를 해결할 수 있습니다. weak self
를 사용해야 하는 경우와 그 사용 방법을 이해하면 앱의 메모리 사용 효율성을 높이고 예기치 않은 크래시를 방지할 수 있습니다. 위에서 설명한 내용을 기반으로 weak self
를 올바르게 사용하여 안정적인 코드를 작성할 수 있도록 노력합시다.