Refactoring (리펙토링)
[iOS - swift] 2. weak self 동작 이해하기 - 외부에 weak self 선언하고 클로저에서 사용하는 경우 (#캡처리스트 [weak self] 리펙토링)
jake-kim
2023. 11. 5. 01:19
1. weak self 동작 이해하기 - retain cycle이 발생하는 경우
2. weak self 동작 이해하기 - 외부에 weak self 선언하고 클로저에서 사용하는 경우 (#캡처리스트 [weak self] 리펙토링)
weak self를 클로저 외부에서 사용 시 retain cycle이 발생할까?
- 예제를 위해 retain cycle이 발생하는 코드 준비
class A {
private var closureEscaper: ((String) -> ())?
func escape(closure: @escaping (String) -> ()) {
print("escaping!")
closureEscaper = closure
}
}
class B {
var name = "Jake"
let a = A() // 1. B에서 A참조
init() {
// 클로저 내부에서 strong self 참조: retain cycle 발생 o
a.escape { string in
self.name = string // 2. A에서 B참조
}
}
deinit {
print("DEINIT: B")
}
}
- 여기서 B인스턴스를 만든 후 nil을 할당해도 메모리 해제 x
var b: B? = B()
b = nil
// deinit이 안불림
- 외부에서 weak self 선언 시 retain cycle이 발생하지 않아서 b인스턴스는 deinit 호출
weak var weakSelf = self
a.escape { string in
weakSelf?.name = string
}
var b: B? = B()
b = nil
/*
escaping!
DEINIT: B
*/
- 외부에서 weak self한 후 옵셔널 바인딩: strong self로 다시 참조하므로 retain cycle 발생 o
weak var weakSelf = self
guard let weakSelf else { return }
a.escape { string in
weakSelf.name = string
}
// deinit이 안불림
weak self 리펙토링
- 하나의 함수 안에서 closure가 있는 함수를 사용하는 곳이 여러개가 있어, weak self를 다수 쓰는 경우가 존재
class ViewController: UIViewController {
var someValue = 0
override func viewDidLoad() {
super.viewDidLoad()
someFunc1 { [weak self] in
print(self?.someValue)
}
someFunc2 { [weak self] in
print(self?.someValue)
}
someFunc3 { [weak self] in
print(self?.someValue)
}
}
func someFunc1(block: @escaping () -> ()) {
// Some working...
}
func someFunc2(block: @escaping () -> ()) {
// Some working...
}
func someFunc3(block: @escaping () -> ()) {
// Some working...
}
- [weak self]를 매번 선언해주어도 되지만, 코드 중복으로 인해 외부에 weak self를 선언하여 사용해도 retain cycle없이 사용이 가능
- weak self를 매번 선언해주지 않아도 아래처럼 간결하게 처리가 가능
weak var weakSelf = self
someFunc1 {
print(weakSelf?.someValue)
}
someFunc2 {
print(weakSelf?.someValue)
}
someFunc3 {
print(weakSelf?.someValue)
}
주의사항) weak로 잡은 상태에서 옵셔널 바인딩하는 경우
- 보통 클로저 안에서 아래처럼 옵셔널 바인딩을 수행
someFunc1 { [weak self] in
guard let self else { return }
print(someValue)
}
- 하지만 외부에 weak self를 선언한 상태에서 다시 옵셔널 바인딩을 시도하면 strong self로 잡히는 것을 주의
weak var weakSelf = self
guard let weakSelf else { return } // <- strong self로 잡히므로 이 weakSelf를 closure안에서 쓰면 retain cycle
someFunc1 {
print(weakSelf.someValue)
}
someFunc2 {
print(weakSelf.someValue)
}
someFunc3 {
print(weakSelf.someValue)
}
핵심 정리
- 1). 클로저 내부에서 strong self 참조: retain cycle 발생 o
a.escape { string in
self.name = string
}
- 2). 외부에서 weak self: retain cycle 발생 x
weak var weakSelf = self
a.escape { string in
weakSelf?.name = string
}
- 3). 외부에서 weak self한 후 옵셔널 바인딩: strong self로 다시 참조하므로 retain cycle 발생 o
weak var weakSelf = self
guard let weakSelf else { return }
a.escape { string in
weakSelf.name = string
}
* 전체 코드: https://github.com/JK0369/ExWeakSelf