Swift

클로저에서 [weak self] 사용할 때 주의할 점3

Phililip
728x90

안녕하세요.

 

[weak self] 관해서 얘기가 좀 많은가 봐요..허헣...

 

아래 글에서 [weak self] 관련한 얘기들을 다뤄봤는데요,

 

클로저에서 [weak self] 사용할 때 주의할 점

 

클로저에서 [weak self] 사용할 때 주의할 점

안녕하세요. 클로저 안에서 [weak self]를 쓰는 Swift 코드를 적지 않게 볼 수 있어요. 그 이유는 메모리 누수(memory leak)를 막기 위해서인데요, 만약, 클로저 안에서 [weak self]와 함께 guard-let 구문을 사

phillip5094.tistory.com

 

클로저에서 [weak self] 사용할 때 주의할 점2

 

클로저에서 [weak self] 사용할 때 주의할 점2

안녕하세요. 저번 글에서 [weak self]를 쓸 때 주의할 점에 대해서 알아봤는데요. 클로저에서 [weak self] 사용할 때 주의할 점 클로저에서 [weak self] 사용할 때 주의할 점 안녕하세요. 클로저 안에서 [we

phillip5094.tistory.com

 

 

 

비슷한 내용이지만 관련된 글이 있어서, 한번 내용을 정리해볼까 합니다.

 


이전 글들을 보면, 클로저 또는 중첩된 클로저에서 retain cycle을 막기 위한 방법은 크게 3가지라고 볼 수 있을 것 같아요.

  • self를 그냥 사용해서 strong reference가 걸리는 것을 막는다.
  • self로 override 한다.
  • strongSelf로 바인딩한다.

 

여기에 대한 추가 견해(?)라고 볼 수 있는 내용은 아래와 같습니다.

 

 

 

## 1. self를 그냥 사용해서 strong reference가 걸리는 것을 막는다.

모든 곳에서 self?.foo 처럼 사용하면 됩니다.

 

다만, block 중간에서 self가 언제든지 nil이 될 수 있다는 것을 인지하고 있어야 해요.

 

 

 

 

## 2. self로 override 한다.

클로저 블록 상단에서 guard let self = self else { return } 구문을 사용해서 strong & non-optional 하게 바꿔주는 것이에요.

 

하지만 이 방법을 사용할 경우, 아래처럼 중첩된 클로저의 inner 클로저에서 self를 그대로 사용하지 않도록 주의해야 합니다.

(inner 클로저에서 self를 그대로 사용할 경우 retain cycle이 발생하기 때문이에요.)

self.doSomething = { [weak self] in
    guard let self = self else { return }
    self.doSomethingElse = { // inner 클로저에서 [weak self]를 사용하지 않을 경우, retain cycle이 발생합니다.
        self.foo()
     }
}
self.doSomething()

 

SwiftLint를 사용해서 이런 문제를 미연에 방지할 수 있겠죠??

 

 

 

 

## 3. strongSelf로 바인딩한다.  🤔

결론 먼저 말씀드리면, 선호하지 않는다는 얘기가 많아요.

 

guard let strongSelf = self else { return } 구문을 사용하면, 강한 참조인 strongSelf와 약한 참조인 self?를 block 안에서 자유롭게 사용할 수 있다는 장점은 있지만,

 

오히려 이게 더 헷갈리게 만든달까요??

 

 

아래 코드를 보면,

firstChild.playLater { [weak self] in
    guard let strongSelf = self else { return }
    strongSelf.gamesPlayed += 1
    strongSelf.secondChild.playLater {
        if let strongSelf = self {
            // 👍 Locally bound the weak self reference.
            // (But didn't use the bound variable.)
            print("Played \(self?.gamesPlayed ?? -1) with first child.")
        }
        // ⚠️ Strongly captures `strongSelf` from outside by accident
        // and creates cycle.
        strongSelf.gamesPlayed += 1
        completion(strongSelf.gamesPlayed)
    }
}

 

inner 클로저에서 사용한 if let strongSelf = self { ... } 의 strongSelf와 strongSelf.gamesPayload + = 1 의 strongSelf는 서로 다른 것이에요.

 

strongSelf.gamesPayload += 1 의 strongSelf는 outer 클로저에서 선언한 strongSelf로, 지금 같은 구조라면 retain cycle이 생기게 됩니다.

 

 

 

## 결론

이러저러한 이유 때문에, [weak self] 사용에 대한 견해는  이렇게 정리가 될 것 같아요.

  • non-escaping 클로저에서는 강한 참조인 self를 사용한다. (non-escaping 클로저에서는 retain cycle이 생기지 않기 때문입니다.)
  • 애매하면... [weak self]를 쓴다.
  • 클로저 상단에서 self로 override 한다.

 

 

 

## 참고

- https://christiantietze.de/posts/2022/05/weak-self-consistency/

 

Weak Self -- Closure Rules of Thumb

In Swift, you can weak-ify references to self in escaping closures, and then you need to deal with the case that the reference is gone when the block is called. Last month, Benoit Pasquier and Chris Downie ...

christiantietze.de

 

 


 

이번 글은 여기서 마무리.

 

 

 

반응형

'Swift' 카테고리의 다른 글

inout  (0) 2022.06.05
Modeling errors  (0) 2022.05.27
클로저에서 [weak self] 사용할 때 주의할 점2  (0) 2022.04.24
클로저에서 [weak self] 사용할 때 주의할 점  (0) 2022.04.16
Automatic Reference Counting (ARC)  (0) 2022.04.10