weak self는 무엇일까 ??

이건준·2023년 2월 8일
0

1. 상황설명

  • 취업을 한 지인이 면접을 본 회사분이 말씀하시길 weak self를 굳이 쓰지않아도 되는 경우가 있다는걸 말씀해주셨다고한다

  • 항상 클로저내부에서 self를 이용하여 외부프로퍼티를 참조하는 경우에 매번 weak self 혹은 withUnretained 오퍼레이터를 이용하던 나에게 꽤 큰 충격이였다

weak self를 쓰는 이유 ??

  • 많은 사람들이 알고있듯이 클로저 내부에서 외부 자원을 참조할때에 reference count가 증가하게 되어 계속 증가된 reference count가 감소되지못해 돌고도는 순환참조문제를 해결하기위해 사용된다

그럼 weak self를 쓰지않는 경우는 어떤 경우일까 ??

@inlinable public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
  • 위 코드는 자주 사용되는 고차함수인 map의 시그니처이다. 우린 종종 map을 이용할때 weak self를 사용하곤한다

  • 하지만 map의 시그니처코드를 보면 알 수 있듯이 해당 함수는 @escaping 어노테이션이 붙어있지않음을 확인할 수 있다. 즉 non-escaping 함수라는 것이다

non-escaping 함수라는건 컴파일러는 해당 함수가 종료되고나서 위 클로저가 사용되지않음을 확인한다

-> 즉 map이 종료된 후, 다시 map을 호출한 곳으로 돌아오면 인자로 전달한 클로져는 더 이상 메모리에 올라가 있지 않을 것이라는 것을 의미
-> 애초에 non-escaping함수는 순환참조자체를 만들어낼 수 없는것이다

그렇다면 @escaping을 사용한 클로저 내부에선 weak self를 반드시 사용해야 될까 ??

public func asyncAfter(deadline: DispatchTime, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute work: @escaping @convention(block) () -> Voi
  • DispatchQueue를 이용해서 종종 사용되는 asyncAfter의 시그니처이다. 해당 함수는 보이는것처럼 @escaping을 사용되었다

  • 이런 Escaping클로저에선 다음의 경우에 weak self를 사용하지않으면 순환참조문제가 발생할 수 있다

1. 클로져가 객체의 property에 저장되거나 다른 클로저로 전달될 경우
2. 클로져 안에 있는 객체가 클로져(해당 클로져 혹은 전달 받은 클로져)에 대한 강한 참조를 유지하는 경우

  • 헌데 신기한게 해당 함수에선 순환참조문제를 일으키지않는다. 그 이유는 내가 지정한 시간이후까지 살아남았다가 알아서 해당 클로저는 종료되기때문이다

GCD, UIView.animate, UIViewPropertyAnimator과 같이 animation call은 특정 프로퍼티에 저장하지않는 이상 순환참조문제를 발생시키지않는다

완전 신기한 부분 !!

DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
	let formatted = self?.format(42)
	print(formatted as Any)
}
  • 정말 신기했던 부분은 그럼 GCD클로저 내부에서 weak self를 사용하지않아도되는데 weak self를 사용할때랑 안할때랑 결과값이 다를 수도 있다는것이다

  • 지금 2초를 더 주었기때문에 2초동안 해당 클로저가 죽지않고 살아있을텐데 해당 인스턴스가 2초 이전에 deinit된다면 self가 nil이 될테고 우리는 의도한 값을 얻을 수 없을 것이다

  • 반대로 weak self를 사용하지않는다면 2초 이전에 해당 인스턴스가 deinit이 되더라도 self는 무조건 인스턴스를 바라보고있고 우리가 의도한 값을 가져올 수 있다는 것이다 와...

Optional Chaining vs guard let self = self

  • 정말 코딩을 하면서 무수히 많이 사용했던 코드라 하면은
    guard let self = self else { return }
    위 코드일것이다

  • 헌데 이런 코드가 부작용이 일어날 수 있음을 야기한다고한다. 그 이유가 무엇일까 ??

  • guard let self는 인스턴스가 nil인지 확인한 후 nil이 아니라면 클로저가 실행되는 동안 잠시 strong reference와 동일한 효과를 내어 self가 유지됨을 보장한다

  • 옵셔널 체이닝은 모든 메서드 호출에서 self에 대한 nil 검사를 진행한다.
    즉 클로저 실행 중 어느 시점에서 self가 nil이 되면 자동으로 해당 메서드 호출을 건너뛰고 다음 줄로 이동한다

-> 결국 guard let self는 deallocated를 지연시킬 수 있다는 점이다

참고사이트 [weak self] 무조건 사용하는게 맞는걸까? 🤔
You don’t (always) need [weak self]

0개의 댓글