포스트

(Deep Dive) @ObservedObject vs @StateObject

@ObservedObject vs @StateObject 이부분은 좀 더 자세히 알아봐야할것같아서 이렇게 새롭게 글을 작성한다

코드 예시는 여기를 참고하여 작성을 한다.

@ObservedObject vs @StateObject

우선 둘의 공통점은 ObservableObject 프로토콜을 따른다는 것이다.

CleanShot 2024-11-13 at 20 19 22

그리고 지금 아래 코드를 보면 viewModel에 대해 Wrapper를 다르게 했는데 이렇게 해도 실행 결과는 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
final class CounterViewModel: ObservableObject {
    @Published var count = 0

    func incrementCounter() {
        count += 1
    }
}

struct CounterView: View {
    @ObservedObject var viewModel = CounterViewModel()
    @StateObject var viewModel = CounterViewModel()

    var body: some View {
        VStack {
            Text("Count is: \(viewModel.count)")
            Button("Increment Counter") {
                viewModel.incrementCounter()
            }
        }
    }

}

Nov-13-2024 20-16-04

CleanShot 2024-11-13 at 20 16 37

그러면 차이를 줘보도록 하자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct RandomNumberView: View {
    @State var randomNumber = 0

    var body: some View {
        VStack {
            Text("Random number is: \(randomNumber)")
            Button("Randomize number") {
                randomNumber = (0..<1000).randomElement()!
            }
        }.padding(.bottom)

        CounterView()
    }
}

RandomNumberView를 만들어 준다.

1. @ObservedObject

1
2
3
4
5
6
7
8
9
10
11
12
13
struct CounterView: View {
    @ObservedObject var viewModel = CounterViewModel()

    var body: some View {
        VStack {
            Text("Count is: \(viewModel.count)")
            Button("Increment Counter") {
                viewModel.incrementCounter()
            }
        }
    }

}

Nov-13-2024 20-29-30

카운트만 눌렀을때는 숫자가 증가하지만

랜덤을 누르는순간 카운트가 초기화가 되어버린다.

CleanShot 2024-11-13 at 20 45 12

1
<SwiftUI.CGDrawingView: 0x101390ed0; frame = (160.333 462.333; 81.6667 20.3333); anchorPoint = (0, 0); opaque = NO; autoresizesSubviews = NO; layer = <_TtC7SwiftUIP33_65A81BD07F0108B0485D2E15DE104A7514CGDrawingLayer: 0x600002635b60>>

이제 랜덤을 누르면 어떻게 되는지 확인하자

CleanShot 2024-11-13 at 20 47 13

1
<SwiftUI.CGDrawingView: 0x101390ed0; frame = (160 462.333; 82 20.3333); anchorPoint = (0, 0); opaque = NO; autoresizesSubviews = NO; layer = <_TtC7SwiftUIP33_65A81BD07F0108B0485D2E15DE104A7514CGDrawingLayer: 0x600002635b60>>

CounterViewModel의 메모리가 달라진걸 확인할 수 있다.

즉 랜덤을 누름과 동시에 ViewModel객체가 초기화가 되기에 count도 초기값인 0으로 돌아가는것.

2. @StateObject

1
2
3
4
5
6
7
8
9
10
11
12
13
struct CounterView: View {
    @StateObject var viewModel = CounterViewModel()
    
    var body: some View {
        VStack {
            Text("Count is: \(viewModel.count)")
            Button("Increment Counter") {
                viewModel.incrementCounter()
            }
        }
    }

}

Nov-13-2024 20-30-30

위와 달리 카운트가 증가한상태에서 랜덤을 눌러도 카운트가 유지가 된다.

CleanShot 2024-11-13 at 20 41 19

1
<SwiftUI.CGDrawingView: 0x10130f7d0; frame = (160 462.333; 82 20.3333); anchorPoint = (0, 0); opaque = NO; autoresizesSubviews = NO; layer = <_TtC7SwiftUIP33_65A81BD07F0108B0485D2E15DE104A7514CGDrawingLayer: 0x60000262e100>>

이건 count is와 관련된 View의 정보

숫자를 늘려도 그대로이다. 즉 ViewModel의 메모리가 그대로 유지된채로 값이 바뀐다는것이다.

차이점

랜덤을 누르는 순간 ViewModel의 객체가 초기화가 되었다.

ObservedObject는 초기화가 된 사례 StateObject는 초기화가 되지 않은 사례 이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct RandomNumberView: View {
    @State var randomNumber = 0

    var body: some View {
        VStack {
            Text("Random number is: \(randomNumber)")
            Button("Randomize number") {
                randomNumber = (0..<1000).randomElement()!
            }
        }.padding(.bottom)

        CounterView()
    }
}

여기서 랜덤 버튼을 클릭하게되면 숫자가 바뀐다. 즉 View가 업데이트 된다.

CounterView()는 현재 RandomNumberView의 자식뷰 이므로, 같이 업데이트가 된다.

이때 @ObservedObject를 사용하게 되면 CounterView 자체도 새롭게 렌더링이 되면서 ViewModel을 새롭게 생성하게 된다. 그러면서 메모리가 바뀌게 된것.

하지만 @StateObject를 사용하게 되면 CounterView는 새롭게 렌더링이 될지라도 ViewModel 자체는 그대로 유지를 하기에 값이 변하지 않은것.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.