틈틈히 적어보는 개발 일기

[TIL #3] strong self, weak self, delays deallocation 본문

📝 TIL

[TIL #3] strong self, weak self, delays deallocation

itllbegone 2022. 4. 3. 17:34

이전에 프로젝트를 진행하며 클로저 내부에서 self를 활용해야 했을 때 이 코드를 정말 자주 습관적으로 사용했다.

  • { [weak self] _ in }
  • guard let self = self else { return }

 

클로저 내부에서의 캡쳐링으로 인한 강한 참조를 방지하기 위해 `[weak self]`를 활용하기는 했는데

Optional을 해제하기 위해 저렇게 처리를 하다 보니 문득 이게 맞기는 한건가..? 내가 아무 생각 없이 사용하고 있지는 않는건가..? 라는 생각이 들었었고, 또한 강한 참조에 대해 공부할 필요를 느꼈다.

자연스럽게 `guard let self = self else { ... }` 를 사용하는 이유, 이에 대한 동작 원리, 그리고 `[weak self]`를 부르지 않고 바로 `self`를 참조하여도 이상이 없는 상황에 대해서 알게 되었다.

 

이를 정말 알기 쉽게(다이어그램부분)표현해주었을 뿐만 아니라 읽으면서 큰 도움이 되었던 미디엄을 지인에게 소개받아 정독해보았다.

https://medium.com/@almalehdev/you-dont-always-need-weak-self-a778bec505ef

 

You don’t (always) need [weak self]

We will talk about weak self inside of Swift closures to avoid retain cycles & explore cases where it may not be necessary to capture self weakly.

medium.com

 

[weak self]가 필요한지 아닌지에 대한 다이어그램

- [weak self]

여기서 주목했던 점은 `[weak self]`를 사용하지 않아도 되는 조건인데, escaping closure가 아니면서 `delays deallocation`이 필요 없는 경우에는 `[weak self]`를 사용하지 않아도 된다는 것이다.

delays deallocation은 조금 이따가 서술하도록 하고..

결론적으로 escaping closure가 아니라면, 곧바로 클로저가 실행되고 종료되므로 (like 고차함수들) self가 캡쳐링이 되어도 바로 실행이 끝나고 메모리에서 해제되기 때문에 memory leak이 발생될 여지가 없다.

 

- Delays deallocation

이 경우에는 조금 다른데 의도적으로 해당 객체의 deallocation을 지연시키는 것이다.

`[weak self]`로 클로저 내부에서 캡쳐링을 해도 ARC를 증가시키지 않도록 조치했는데, `guard let self = self else { ... }` 를 사용하면 클로저가 살아있는 한 강한 참조로 인하여 self가 메모리에서 해제되지 않는 현상이 일어난다. 이러한 행위는 의도적으로 '지금 하는 일이 중요해서 무조건 다 끝마쳐야 하니까 메모리에서 지우지 말고 나 끝날때 까지만 기다려~~' 라는 것이다.

즉 `guard let self = self else { ... }` 는 원래라면 `[weak self]`로 인해서 self == nil 이라면 클로저가 실행이 되지 않아야 하는데, self가 살아있을 때 self를 강한 참조로 ARC를 증가시키고, 클로저 이외에 다른 모든 곳에서의 ARC가 감소했고 남은 ARC가 클로저만 존재한다면 해당 클로저의 실행이 끝나고 self를 deallocate 시키겠다는 의미이다.

해당 self 객체가 nil이 되기 이전에 반드시 끝마쳐야 할 경우에(지금 수행하고 있는 일들을 저장한다거나, 시간(코스트)가 많이 드는 요청 혹은 작업의 끝을 봐야 한다거나...)이러한 기법을 통해서 유지시킬 수 있을 것 같다.

Comments