티스토리 뷰
우리가 흔히 사용하는 노트북, 컴퓨터, 핸드폰은 성능이 한정되어 있기 때문에 비용이 많이 들거나,
메모리를 많이 잡아먹는 작업은 최대한 피해 줘야 한다.
따라서 복잡한 계산이나, 비용이 많이 드는 작업에 적합한 lazy var 키워드에 대해 알아보자!
💡 lazy var 이란?
lazy는 lazy키워드를 사용한 변수가 처음부터 메모리에 할당되는 것이 아니라, 필요에 의해 변수가 처음으로 접근이 필요할 때 메모리에 할당되도록 하는 기법이다.
일반 변수랑 다른 점은 뭘까?
lazy var
- 처음 접근이 필요할 때 한번 초기화하면 그 값을 저장하여 이후에도 값을 읽을 때 저장된 값을 할당해 준다.
var or let
- 선언할 때 메모리에 모두 할당되기 때문에, 당장 사용하지 않더라도 모두 메모리에 할당하게 된다.
비교해 보면 위와 같다!
즉, 일반 변수로 선언을 하게 되면 복잡한 로직을 실행할 때 선언한 즉시 실행되기 때문에 사용자가 해당 기능을 실행하지 않았는데도 느려지게 되는 문제가 발생할 수 있다.
반면 lazy 변수로 선언을 하게되면 첫 사용할 때까지 지연되었다가 메모리에 할당되는 방식이기 때문에 불필요한 메모리 리소스를 낭비하지 않아도 된다.
그리고 첫 사용 시에 메모리에 할당되며 초기화가 되기 때문에, 앱을 첫 실행했을 때 초기화 되는 시간과 비용이 많이 절감되기 때문에 앱을 첫 실행했을 때의 시간도 단축시킬 수 있다!
하지만 반드시 좋은 점만 있는 건 아니다
lazy 키워드는 나중에 초기화되어 실행되기 때문에 멀티스레드 환경에서 동시 접근하게 되면 여러 번 초기화가 될 수 있다.
반면 일반 변수는 앱 실행즉시 선언되어 초기화되기 때문에 여러 번 초기화를 고려해야 하는 문제가 없다.
즉 일반변수와 lazy 변수가 필요한 역할과 기능을 잘 이해하면서 사용해야 할 거 같다!
📍주의할 점
1. lazy + var
lazy는 앱을 실행할 때 다 선언을 하는 방식이 아닌, 필요한 시점에 선언을 하고 메모리에 할당하는 방식이다.
따라서 처음에는 값이 존재하지 않으며 이후 필요한 첫 시점에 값이 초기화되며 생성되기 때문에 let이 아닌 var 키워드와 사용되어야 한다.
2. class나 구조체를 사용해야 한다.
lazy 자체가 인스턴스 속성이기 때문에 class나 구조체 내부에서만 사용할 수 있으며, 클래스나 구조체에 의존하지 않는 전역변수나 static에서는 사용할 수 없다.
3. lazy var weak는 사용하지 못한다.
둘 다 메모리 누수를 방지하기 위해 사용되는 목적은 같지만 방식이 다르다.
lazy는 객체가 필요할 때까지 메모리에 할당하지 않고 최초로 해당 변수에 접근할 때 초기화를 하게 되며 한번 저장한 이후에는 그 값이 계속해서 메모리에 유지되어 있다. 그래서 강한 참조를 필요로 함!
반면 weak는 약한 참조로, 객체가 필요 없어질 때를 자동으로 결정해서 메모리에서 해제하는 방식이다.
따라서 lazy 변수는 강한 참조를 필요로 하는데 weak를 사용하게 되면 그 객체가 언제든지 해제될 수 있기 때문에 둘이 같이 사용할 수 없게 되는 것이다.
✔️ 예제를 통해 알아보기
lazy var를 사용했을 때 정말 필요한 첫 시점에 생성되는지, 이로 인해 메모리낭비를 줄이고 성능이 향상되는지 간단한 예제로 파악해보고자 하였다.
UI를 통해 확인해 보면 위와 같이 연산 작업에 대한 버튼을 눌러주면 결과가 나오는 간단한 예제이다.
import SwiftUI
struct LazyExampleView: View {
@StateObject private var model = ArithmeticModel()
@State private var result: Int?
var body: some View {
VStack {
Button(action: {
result = model.addition
}) {
Text("Addition")
}
Button(action: {
result = model.subtraction
}) {
Text("Subtraction")
}
Button(action: {
result = model.multiplication
}) {
Text("Multiplication")
}
Button(action: {
result = model.division
}) {
Text("Division")
}
if let result = result {
Text("Result: \(result)")
.padding()
}
}
.onAppear {
print("뷰 생성됨")
}
}
}
각각의 버튼을 눌렀을 때 model에 해당하는 연산이 언제 수행되는지를 확인해보고자 한다.
lazy var를 사용했을 경우
class ArithmeticModel: ObservableObject {
lazy var addition: Int = {
print("더하기 실행 중")
return 15 + 3
}()
lazy var subtraction: Int = {
print("뺄셈 실행 중")
return 20 - 8
}()
lazy var multiplication: Int = {
print("곱셈 실행 중")
return 4 * 7
}()
lazy var division: Int = {
print("나눗셈 실행 중")
return 20 / 4
}()
}
실행 동작을 확인해 보면 앱이 제일 처음 실행되고 나서 '뷰 생성됨'이라는 print문만 찍힌다.
이후에 각 연산에 해당하는 버튼을 눌렀을 때 변수가 선언되고 메모리에 할당되면서 선택한 연산과 관련된 Print문이 콘솔에 찍히는 것을 확인할 수 있다.
즉, 앱 실행 즉시 변수가 선언되는것이 아니라 필요에 의한 시점에서 선언된다는 것!!!
lazy var를 사용하지 않았을 경우
처음에 시뮬레이터가 생성되자마자 위와 같은 print가 찍힌다. (아무 버튼을 누르지 않아도)
lazy 변수가 아닌 일반 변수를 사용했기 때문에 앱이 실행되는 시점에서 모든 변수가 선언되어 버리기 때문에 아직 연산을 처리하는 버튼을 누르지 않았는데도 불구하고 모든 변수가 다 생성되어 버리는 것을 확인할 수 있었다.
🛠️ Instrument로 확인해 보기
위에서 print 출력을 통해 변수가 언제 선언되는지를 확인할 수 있었다.
하지만 정말 메모리에도 필요한 첫 시점에 할당되는지 확인하고자 xcode에서 제공하는 Instruments도구를 활용해 확인해 보자
앱의 전체 실행동안에 객체의 생성과 해제, 메모리 사용량을 추적할 수 있는 Allocations를 통해
lazy키워드를 사용하기 전과 후를 비교해서 메모리의 사용량을 알아보면 메모리 할당 상황을 추적하기 좋을 거 같았다.
나는 4가지의 상황을 고려해서 추적을 해보았다.
- 앱 실행 직후
- 첫 번째 lazy 변수 호출 후
- 아무 동작도 하지 않은 lazy 변수 호출 전
- 두 번째 lazy 변수 호출 후
앱실행 직후에는 초기 앱 실행에 필요한 메모리가 8.86 MiB정도 사용된 것을 확인할 수 있었으며 첫 번째 lazy 변수를 호출했을 때 879.62 KiB만큼의 메모리가 추가 할당된 것을 확인할 수 있었다.
그리고 아무 동작도 하지 않았을 때는 128 bytes만큼의 메모리를 유지하고 있었고, 두 번째 lazy 변수를 호출했을 때 54.14 KiB만큼의 메모리를 사용한 것을 확인할 수 있었다.
따라서 lazy변수를 사용했을 때 실제 앱 실행 즉시 메모리에 할당되는 것이 아니라, 필요한 첫 시점에 메모리에 할당되는 것을 직접 확인할 수 있었고 이로 인한 앱 최적화를 고려해 볼 수 있을 것 같다.
참고
https://medium.com/@kyuchul2/lazy-var%EC%99%80-weak-22896de2a4e2
'iOS > Swift' 카테고리의 다른 글
[iOS] @MainActor와 DispatchQueue의 차이 (0) | 2025.03.06 |
---|---|
[iOS/Swift] @escaping closure (1) | 2024.09.26 |
[iOS/Swift] Closure (0) | 2024.05.08 |
[swiftUI] NSPredicate (0) | 2024.03.30 |
[Swift] Optional (1) | 2024.03.28 |
- Total
- Today
- Yesterday
- CoreData
- securefield
- UIKit
- ObservableObject
- ios
- LazyVGrid
- CustomCalendar
- Fastlane
- 백준
- combine
- rxswift
- Xcode
- 스위프트
- imagepicker
- 클로저
- XCTest
- mlmodel
- 16173
- SWIFT
- mergeconflict
- swiftUI
- ScrollViewReader
- 가장가까운같은글자
- 코딩테스트
- 프로그래머스
- MainActor
- 병합충돌
- closure
- 둘만의 암호
- pbxproj
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |