SwiftUI에서 @ObservedObject와 @StateObject는 데이터 흐름을 관리하는 중요한 속성 래퍼이다. 각각의 개념 예시 그리고 차이점에 대해서 알아보자.
@ObservedObject
먼저 @ObservedObject는 외부에서 생성되고 관리되는 관찰 가능한 객체를 뷰에 연결하는데 사용된다.
이 객체는 ObservedObject 프로토콜을 준수해야 하며, @Published 속성이 변경될 때 즉 객체가 변경될 때 마다 뷰를 자동으로 업데이트 하도록 한다.
class UserViewModel: ObservableObject {
@Published var username: String = ""
@Published var isLoggedIn: Bool = false
}
struct UserView: View {
@ObservedObject var viewModel: UserViewModel
var body: some VIew {
VStack {
TextField("Username", text: $viewModel.username)
Text("Login Status: \\(viewMoel.isLoggedIn ? "Logged In" : "Logged Out"
}
}
}
@ObservedObject는 주로 상위 뷰에서 하위 뷰로 데이터를 전달할때 사용된다 이를 통해서 여러 뷰에서 동일한 데이터 소스를 공유할 수 있다.
struct ParentView: View {
@StateObject private var viewModel = UserViewModel()
var body: some View {
VStack {
UserView(viewModel: viewModel)
SettingsView(viewModel: viewModel)
}
}
}
@ObservedObject는 약한 참조를 사용한다. 메모리 관리 측면에서 매우 중요한 부분인데 뷰가 사라질때 @ObservedObject로 선언된 객체가 자동으로 해제될 수 있도록 한다.
@ObservedObject는 ObservedObject의 objectWillChange 퍼블리셔를 사용하여 뷰 업데이트를 트리거 한다. @Published 속성이 변경될 때 마다 이 퍼블리셔가 이벤트를 발생시키고, SwiftUI는 이를 감지하여 뷰를 다시 렌더링 한다.
주의해야할 점은 뷰가 자주 재생성되는 경우에 @ObservedObject를 사용한다면 객체가 매번 새로 초기화 되어 성능 문제가 발생할 수 있다.
List나 ForEach 내부의 뷰에서 @ObservedObject를 사용할 때는 특히 주의해야 하는데 이러한 상황에서는 @StateObject를 사용하는 것이 더 안정적일 수 있다.
복잡한 뷰 구조에서 @ObservedObject를 사용할 때는 데이터 흐름을 명확히 관리해야한다. 환경 객체인 (@EnvironmentObject)나 의존성 주입을 고려해 볼 수 있다.
struct ComplexView: View {
@ObservedObject var viewModel: ComplexViewModel
var body: some View {
VStack {
SubView1(data: viewModel.data1)
SubView2(data: viewModel.data2)
SubView3(data: viewModel.data3)
}
}
}
@StateObject
@StateObject는 SwiftUI 중 뷰 내부에서 관찰 가능한 객체를 생성하고 관리하는데 사용되는 속성 래퍼이다.
iOS14 부터 도입된 이 래퍼는 뷰의 생명주기 동안 객체의 상태를 안정적으로 유지하는데 중요한 역할을 한다.
@StateObject는 ObservableObject 프로토콜을 준수하는 객체를 생성하고 해당 객체의 수명을 뷰의 수명과 일치시킨다. 뷰가 다시 생성되더라도 @StateObject로 선언된 객체는 유지된다.
class DataManager: ObservableObject {
@Published var items: [String] = []
func fetchItems() {
items = ["Apple", "Banana", "Cherry"]
}
}
struct ContentView: View {
@StateObject private var dataManager = DataManager()
var body: some View {
List(dataManager.items, id: \\.self) { item in
Text(item)
}
.onAppear {
dataManager.fetchItems()
}
}
}
@StateObject는 뷰의 생명주기 동안 단 한번만 초기화 된다. 이는 뷰가 여러번 다시 그려지더라도 객체의 상태가 보존된다는 것을 의미한다.
@StateObject는 강한 참조를 사용한다. 이는 뷰가 메모리에 존재하는 동안 객체가 계속 유지됨을 보장한다.
@StateObject를 사용하면 뷰가 다시 생성될 때 마다 객체를 재생성하는 것을 방지할 수 있어 성능이 향상된다. 특히 복잡한 초기화 로직이 있는 객체에 유용하다.
class ExpensiveDataProcessor: ObservableObject {
@Published var result: String = ""
init() {
// 복잡하고 시간이 걸리는 초기화 과정
print("Expensive initialization")
}
func process() {
// 데이터 처리 로직
}
}
struct ProcessingView: View {
@StateObject private var processor = ExpensiveDataProcessor()
var body: some View {
Text(processor.result)
.onAppear {
processor.process()
}
}
}
@StateObject 를 사용하면 뷰가 다시 그려질 때마다 데이터가 초기화 되는 것을 방지할 수 있다. 이는 사용자 경험을 향상 시키고 예기치 않은 데이터 손실을 방지한다.
@StateObject는 주로 뷰 내부에서 객체를 생성할 때 사용된다. 그러나 때로는 외부에서 생성된 객체를 주입받아야 할 수도 있다.
swiftCopystruct ConfigurableView: View {
@StateObject private var viewModel: ViewModel
init(viewModel: ViewModel? = nil) {
_viewModel = StateObject(wrappedValue: viewModel ?? ViewModel())
}
var body: some View {
// 뷰 내용
}
}
@StateObject는 종종 @Published, @Binding, 그리고 @EnvironmentObject와 함께 사용된다. 이렇게 조합하여 복잡한 데이터 흐름을 관리할 수 있다.
class AppState: ObservableObject {
@Published var user: User?
}
struct RootView: View {
@StateObject private var appState = AppState()
var body: some View {
if let user = appState.user {
UserView(user: user)
} else {
LoginView()
}
}
}
@StateObject는 뷰의 프로퍼티로만 사용해야한다. 지역 변수나 계산된 프로퍼티에서는 사용할 수 없다.
뷰의 Init()메서드에서 @StateObject를 직접 초기화 하지 않도록 주의해야 한다.
@ObservedObject와 @StateObject의 차이점
생명 주기 관리
- ObservedObject : 뷰가 재생성될 때 마다 새로운 인스턴스가 생성될 수 있다. 뷰의 생명주기와 독립적으로 관리된다.
- StateObject : 뷰의 생명주기 동안 한번만 생성되고 유지된다. 뷰가 재생성되어도 동일한 인스턴스가 유지된다.
소유권과 생성 위치
- ObservedObject : 외부에서 생성되고 관리되는 객체에 사용된다. 주로 상위 뷰나 다른 컴포넌트에서 생성된 객체를 전달받을 때 사용한다.
- StateObject : 뷰 내부에서 생성되고 관리되는 객체에 사용된다. 해당 뷰가 객체의 소유자가 됩니다.
메모리 관리
- ObservedObject : 약한 참조를 사용한다. 메모리 관리가 더 유연하지만 객체가 예기치 않게 해제될 수 있다.
- StateObject : 강한 참조를 사용한다. 뷰가 존재하는 동안 객체가 메모리에서 해제되지 않음을 보장한다.
성능 특성
- ObservedObject : 뷰가 자주 재생성되는 경우 성능 저하가 발생할 수 있다. 객체가 자주 재초기화될 수 있어 비용이 큰 초기화 로직이 반복될 수 있다.
- StateObject : 뷰가 재생성되어도 객체를 유지되므로 성능이 더 안정적이다. 복잡한 초기화 로직을 가진 객체에 특히 유용하다.
데이터 일관성
- ObservedObject : 뷰가 재생성될 때 데이터가 초기화될 위험이 있다. 데이터의 일관성을 유지하기 위해 추가적인 로직이 필요할 수 있다.
- StateObject : 뷰가 재생성되어도 데이터가 안정적으로 유지된다. 데이터의 일관성을 자동으로 보장한다.
적합한 사용 방식
- ObservedObject : 여러 뷰에서 공유되는 데이터 모델에 적합하다. 의존성 주입 패턴을 구현할 때 유용하다.
- StateObject : 뷰 특정적인 상태나 뷰 모델을 관리할때 적합하다. 싱글톤 패턴이나 전역 상태 관리에 유용하다.
초기화 방식
- ObservedObject : 주로 생성자를 통해 외부에서 주입 받는다.
- StateObject : 뷰 내부에서 직접 초기화 한다.
SwiftUI의 업데이트 메커니즘과 상호작용
- ObservedObject : SwiftUI의 뷰 업데이트 주기에 더 민감할 수 있다. 부모 뷰의 상태 변화에 따라 재생성될 가능성이 높다.
- StateObject : SwiftUI의 뷰 업데이트 주기와 독립적으로 동작한다. 뷰모뷰의 상태 변화에 영향을 받지 않고 안정적으로 유지된다.
'◽️ Programming > SwiftUI' 카테고리의 다른 글
[SwiftUI] @StateObject , @ObservedObject, @EnviromentObject의 역할과 차이점을 자세하게 알아보자 (1) | 2024.07.23 |
---|---|
[SwiftUI] State , Binding (0) | 2024.07.20 |
[SwiftUI] ZStack vs overlay vs background 의 차이점을 알아보자 (0) | 2024.07.18 |
[SwiftUI] - LazyVGrid & LazyHGrid (0) | 2024.07.17 |
[SwiftUI] Networking , Opserver Pattern ( ObservableObject, Published ) (0) | 2024.07.07 |