Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- 리펙토링
- 스위프트
- SWIFT
- swift documentation
- 클린 코드
- Human interface guide
- MVVM
- Refactoring
- collectionview
- Protocol
- uitableview
- map
- 리펙터링
- HIG
- Clean Code
- UITextView
- 리팩토링
- Observable
- clean architecture
- RxCocoa
- uiscrollview
- Xcode
- 애니메이션
- combine
- UICollectionView
- ribs
- swiftUI
- ios
- rxswift
- tableView
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[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.text = self.text // self를 안쓰면 컴파일에러 발생
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
2)
private let text = "label"
private lazy var label: UILabel = {
let label = UILabel()
label.text = text // self를 안써도 컴파일 에러 x
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
1) escaping 클로저
- 클로저 안에서 self 키워드를 붙여서 다른 프로퍼티 접근 (self 키워드 없을 시 컴파일에러 발생)
- self의 reference count가 늘어나는 동시에, 해당 클로저는 바로 사라지지 않으므로 self에 관해 reference count 유지
- 동시에 self에서도 해당 label을 참조하므로 retain cycle 발생
private let text = "label"
private lazy var label: () -> UILabel = {
let label = UILabel()
label.text = self.text // self를 안쓰면 컴파일에러 발생
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
- 예시
- 버튼을 눌렀을 때 VC2를 띄우게 해놓고 다시 VC2를 dismiss 시켰을때 deinit이 호출되는지(메모리 해제가 되는지) 체크
- > deinit이 안불리고 있으므로 retain cycle 발생
class VC2: UIViewController {
private lazy var label: () -> UILabel = {
let label = UILabel()
label.text = self.text
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
private let text = "label"
deinit {
print("DEINIT: VC2")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
let label = label()
view.addSubview(label)
NSLayoutConstraint.activate([
label.centerYAnchor.constraint(equalTo: view.centerYAnchor),
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
])
}
}
- 발생한 retain cycle 형태
- text와 viewDidLoad는 self이므로 결국 아래처럼 retain cycle 형태
- VC2를 참조하고 있는 인스턴스가 VC2를 dismiss 시켜도, VC2의 인스턴스는 retain cycle이 발생했으므로 reference count가 0이 아니게되어 메모리 릭 발생
2) non-escaping 클로저
- 아래 클로저는 괄호 끝에 ()가 있으므로 즉시 적용된 클로저로 수행하는 것으로 인식되어, 컴파일러도 non-escaping으로 받아들여 self키워드도 사용하지 않고 사용 가능
private let text = "label"
private lazy var label: UILabel = {
let label = UILabel()
label.text = text // self를 안써도 컴파일 에러 x
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
- 예시
- retain cycle이 없으므로 dismiss 하면 바로 메모리 해제 ('DEINIT: VC3' 출력)
class VC3: UIViewController {
private lazy var label: UILabel = {
let label = UILabel()
label.text = text
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let text = "label"
deinit {
print("DEINIT: VC3")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
view.addSubview(label)
NSLayoutConstraint.activate([
label.centerYAnchor.constraint(equalTo: view.centerYAnchor),
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
])
}
}
// dismiss하면 DEINIT: VC3 출력
결론
- 클로저 끝에 ()가 붙어있으면 해당 클로저가 지연되지 않고 즉시 실행
- 클로저가 지연되지 않고 즉시 실행된다는 의미?
- 컴파일러가 파악할 수 있으므로 따로 self키워드를 사용하지 않고 사용할 수 있고 retain cycle도 발생하지 않음
- 클로저 끝에 ()가 붙어있지 않은 lazy var는 escaping되며 retain cycle이 발생
* 전체 코드: https://github.com/JK0369/ExLazyInit
'iOS 응용 (swift)' 카테고리의 다른 글
Comments