# 1. 클로저가 바로 실행되는 경우 -> retain cycle 없음.
lazy var 클로저가 곧바로 실행되는 경우, 컴파일러에 의해 클로저는 @nonescape 처리가 되어 self를 캡처하지 않는다.
즉, retain cycle이 발생하지 않는다.
예를 들어, 아래처럼 lazy var의 클로저가 곧바로 실행된다면 retain cycle이 증가하지 않고,. 변수에 nil이 설정되면 deinit이 호출된다.
import UIKit | |
class MyView { | |
private let title = "text" | |
private lazy var label: UILabel = { | |
let label = UILabel() | |
label.text = self.title // self를 capture하지 않음! | |
return label | |
}() | |
func setup() { | |
print("Before using lazy var. retain Count => \(CFGetRetainCount(self))") // Before using lazy var. retain Count => 3 | |
let label = self.label | |
print("After using lazy var. retain Count => \(CFGetRetainCount(self))") // After using lazy var. retain Count => 3 | |
} | |
deinit { | |
print("Deinit!") | |
} | |
} | |
var myView: MyView? = MyView() | |
myView?.setup() | |
myView = nil // Print("Deinit") |
# 2. 클로저 자체를 lazy var로 정의한 경우 -> retain cycle 발생할 수 있음.
클로저는 reference type이다.
클로저 자체를 lazy var로 정의하면, lazy var를 사용하는 곳에서 클로저 인스턴스를 lazy 하게 생성된다.
생성된 후 클로저 내부가 실행되므로 클로저는 @escaping이다.
즉, 클로저 내부에서 self를 capture 하기 때문에 retain cycle이 발생할 수 있다. -> retain count 증가함
class MyView { | |
private let title = "text" | |
private lazy var label: () -> (UILabel) = { | |
let label = UILabel() | |
label.text = self.title // @escaping 클로저로 처리되어 self가 capture 됨. | |
return label | |
} | |
func setup() { | |
print("Before using lazy var. retain Count => \(CFGetRetainCount(self))") // Before using lazy var. retain Count => 3 | |
let label = self.label() | |
print("After using lazy var. retain Count => \(CFGetRetainCount(self))") // After using lazy var. retain Count => 4 | |
} | |
deinit { | |
print("Deinit!") | |
} | |
} | |
var myView: MyView? = MyView() | |
myView?.setup() | |
myView = nil // deinit 호출되지 않음! |
이때 클로저에서 [unowned self]나 [weak self]를 사용하면 retain cycle을 방지할 수 있다.
import UIKit | |
class MyView { | |
private let title = "text" | |
// 클로저에서 [unowned self]를 사용해서 retain cycle 방지 | |
private lazy var label: () -> (UILabel) = { [unowned self] in | |
let label = UILabel() | |
label.text = self.title | |
return label | |
} | |
func setup() { | |
print("Before using lazy var. retain Count => \(CFGetRetainCount(self))") // Before using lazy var. retain Count => 3 | |
let label = self.label() | |
print("After using lazy var. retain Count => \(CFGetRetainCount(self))") // After using lazy var. retain Count => 3 | |
} | |
deinit { | |
print("Deinit!") | |
} | |
} | |
var myView: MyView? = MyView() | |
myView?.setup() | |
myView = nil // Print("Deinit") |
# 참고
[iOS - swift] lazy var 클로저 사용 주의 (리테인 사이클, 메모리 릭)
Lazy var 클로저 사용시 주의사항 lazy var 클로저 사용 시 retain cycle이 발생하는지? 아래 1)번과 2)번 구분 (아래에서 계속) 1) private let text = "label" private lazy var label: () -> UILabel = { let label = UILabel() label.
ios-development.tistory.com
Does lazy var capture self?
No. Immediately applied closure is automatically considered @noescape.
frouo.com
이번 글은 여기서 마무리.
'Swift' 카테고리의 다른 글
JSON을 enum associated value로 Decoding 하기 (1) | 2024.09.03 |
---|---|
map, compactMap, flatMap (0) | 2024.08.18 |
@propertywrapper (0) | 2024.06.07 |
protocol initializer가 클래스에서 required로 정의되어야 하는 이유 (0) | 2024.05.12 |
propertyWrapper로 UserDefaults 관리 (0) | 2024.05.11 |