면접 질문: Closure에서 weak self를 사용하지 않아도 되는 경우?

2024. 9. 8. 15:24iOS/Swift 문법

이 말은 달리 말하면 순환 참조가 일어나는 경우는 언제냐는 질문과 같다. 순환 참조는 두 인스턴스가 서로 강한 참조할 때 발생하고, 메모리 해제되지 않아 누수가 발생한다. swift에서는 이를 회피하기 위해 캡처리스트를 이용한다. 캡처 리스트에서 weak 또는 unowned self를 사용할 수 있다.

TL;DL

클로저의 생명 주기가 self보다 짧거나 동시에 해제되는 경우 사용하지 않아도 된다.

weak self를 사용하는 경우

  • 클로저가 self를 강하게 참조하고, 그 클로저가 self의 프로퍼티로 저장되거나 self가 클로저와 긴밀하게 연결되는 경우
  • 콜백, 타이머, 네트워크 요청과 같은 비동기 작업에서 weak self를 사용한다.
class SomeClass { 
	var completionHandler: (() -> Void)? 
    
    func doSomething() { 
    	completionHandler = { 
        // self를 강하게 참조하고 있음 self.doTask() 
        } 
    } 

	func doTask() { 
    	print("Task Done") 
    } 
}

weak self를 사용하지 않아도 되는 경우

  • 클로즈의 생명 주기가 self보다 짧거나 동시에 해제되는 경우
  • 순환참조를 일으키지 않는 경우
  • 짧은 시간 내에 실행되고 바로 해제되는 경우
  • 강한 잠조 없이 사용되는 경우
    class MyViewController: UIViewController {
      func doSomething() {
          UIView.animate(withDuration: 1.0) {
              self.view.backgroundColor = .blue
          }
      }
    }

애니메이션은 실행되고 즉시 해제 되기 때문에 순환 참조가 발생하지 않는다.

// Firebase Combine Community // https://github.com/firebase/firebase-ios-sdk/blob/main/FirebaseCombineSwift/DECISIONS.md 
extension Auth {
    public func createUser(withEmail email: String,
                           password: String) -> Future<AuthDataResult, Error> {
      Future<AuthDataResult, Error> { /* [weak self]  <-- not required */ promise in
        self?.createUser(withEmail: email, password: password) { authDataResult, error in
          if let error {
            promise(.failure(error))
          } else if let authDataResult {
            promise(.success(authDataResult))
          }
        }
      }
    }
}

Firebase iOS SDK의 Combine community repository에서 weak self를 제거하였다.

After discussing internally, we came to the conclusion that the outer closure in the following piece of code is non-escaping, hence there is no benefit to weakly capture self. As the inner closure doesn't refer to self, the reference does not outlive the current call stack.
It is thus safe to not use [weak self] in this instance.

non escaping closure이기 때문에, weak self capture할 필요가 없다.