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 |
Tags
- Observable
- 리펙터링
- tableView
- RxCocoa
- 리펙토링
- uitableview
- Clean Code
- 리팩토링
- map
- collectionview
- 클린 코드
- Protocol
- MVVM
- combine
- rxswift
- UITextView
- uiscrollview
- UICollectionView
- SWIFT
- swiftUI
- 애니메이션
- swift documentation
- Refactoring
- Human interface guide
- ios
- clean architecture
- HIG
- 스위프트
- ribs
- Xcode
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] weak self 알고쓰기 (escaping closure, memory leak, weak self를 사용해도 crash가 나는 이유) 본문
iOS 기본 (swift)
[iOS - swift] weak self 알고쓰기 (escaping closure, memory leak, weak self를 사용해도 crash가 나는 이유)
jake-kim 2022. 3. 31. 22:45사전 지식1) capture와 escaping의 개념
- 공통점: 둘 다 closure에서 사용되는 개념
- 차이점
- capture: 클로저 내부에서 밖에 있는 scope의 instance를 참조하는 것
- escaping: 클로저 외부에서 해당 클로저 자체를 참조하고 있는 것
사전 지식2) Escaping closure의 의미
- `저장`되고, `지연`시킬 수 있는 기능 블록을 가지는 클로저가 바깥의 변수에 의해서 저장되는 경우 "Escaping closure"라고 정의
- 인자로 전달받은 함수 중 함수의 리턴 이후에 실행될 수 있는 함수
- 함수가 리턴되면 해당 scope은 사라지지만, closure는 함수의 scope을 escaping하여 함수 종료 후에 실행된다는 의미
// 함수의 return보다 completion이 늦게 실행되는 경우 (escaping closure)
func someFunction(completion: @escpaing () -> Void = {}) {
self.someDelayProcess {
completion()
}
print("someFunc!")
return
}
- 만약 아래처럼 단순히 return 전에 closure가 실행되는 경우에는 non-escaping
func someFunction(completion: () -> Void = {}) {
completion()
print("someFunc!")
return
}
사전 지식3) guard let self = self 사용 시 주의할 점
- 아래 그림에서 someView의 reference count는 몇개?
- reference count는 현재 weak로만 잡혀있어서 0개인것 같지만, view2가 view1을 strong으로 잡고 있으므로 사실상 reference count가 한개 증가된 상태
// 코드로 알아보는 retain count
weak var view1: UIView?
var view2: UIView?
let sampleView = UIView()
print(CFGetRetainCount(sampleView)) // 2
self.view1 = sampleView
print(CFGetRetainCount(sampleView)) // 2
self.view2 = self.view1
print(CFGetRetainCount(sampleView)) // 3
- 만약 메소드 내부에서 잡으면, 메소드가 종료되는 동시에 내부에서 strong으로 잡아도 reference count가 감소
- 아래에서 알아볼 guard let self = self else { return }도 이처럼 캡쳐리스트로 weak self로 잡은 후 내부에서 strong으로 잡아도 결국 해당 클로저 종료 후 메모리 해제
// 메소드 안에서 strong으로 잡아도 메소드 scope이 종료되면 자동으로 RC가 줄어들음
print(CFGetRetainCount(self.sampleView)) // 2
self.someFunc()
print(CFGetRetainCount(self.sampleView)) // 2
func someFunc() {
weak var localWeakView = self.sampleView
print(CFGetRetainCount(self.sampleView)) // 2
let localStrongView = self.sampleView
print(CFGetRetainCount(self.sampleView)) // 3
}
- weak self를 사용한 클로저에서, gaurd let ss = self else { return } 적용 시 현재 self를 weak로 잡아놓았지만, guard let ss로 strong으로 잡아버리면 self의 reference count가 증가된 형태
- 하지만 해당 reference count는 밖의 self를 weak로 잡은 다음 내부 scope에서 다시 strong으로 잡은 것이므로 되어 해당 scope이 끝나면 자동으로 reference count는 감소할 것
- 만약 escaping closure에서 처음부터 weak self를 안쓰고 strong으로 잡아놓는다면 해당 클로저 scope에서 strong으로 잡게되어서 reference count가 감소되지 않을 것
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(CFGetRetainCount(self)) // 29
self.process { [weak self] in
print(CFGetRetainCount(self)) // 29
guard let ss = self else { return }
print(CFGetRetainCount(self)) // 30
}
}
func process(completion: () -> Void = {}) {
completion()
}
}
weak self 알고 사용하기
- escaping closure이 아닌 경우
- self가 클로저 실행보다 늦게 dealloc되면 weak self안써도 무방
- 만약 self가 클로저보다 먼저 dealloc되면, 클로저 내부에서 self를 참조하는 순간 크래시 발생하므로 weak self 사용할 것
- escaping closure인 경우
- weak self 사용할 것
* 참고
https://medium.com/@almalehdev/you-dont-always-need-weak-self-a778bec505ef
'iOS 기본 (swift)' 카테고리의 다른 글
Comments