iOS/SwiftUI

@StateObject vs @ObservedObject 차이점

Brad_Heo 2023. 1. 7. 09:58

@StateObject 및 @ObservedObject 프로퍼티 래퍼는 SwiftUI 보기에 관촬된 개체의 변경 사항에 대한 응답으로 업데이트하도록 지시합니다. 두 래퍼는 비슷해 보이지만 SwiftUI에서 앱을 빌드할때 중요한 차이점이 있습니다.

What is an @ObservedObject?

@StateObject와 @ObservedObject의 차이점을 알아보기 전 @ObservedObject가 무엇인지 이해하는 것이 좋습니다. 두 프로퍼티 래퍼 모두 개체가 ObservableObject 프로토콜을 준수해야 합니다. 이 프로토콜은 객체가 변경되기 전 방출하는 게시자가 있는 객체를 나타내며 SwiftUI가 뷰를 다시 그리기를 그리도록 지시할 수 있습니다.

ObservableObject를 준수하는 타입은 @ObservedObject 프로퍼티 래퍼와 결합하여 SwiftUI 뷰를 연결하고 관찰된 객체 내에서 감지된 변경 후에 다시 그리도록 할 수 있습니다.

예를 들기 좋은 카운터 뷰 예제 입니다.

final class CounterViewModel: ObservableObject {
    @Published var count = 0

    func incrementCounter() {
        count += 1
    }
}

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

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

CounterViewModel이 ObservableObject프로토콜을 따르기 때문에 뷰 모델을 @ObservedObject로 정의할 수 있습니다. 뷰 모델 내부의 count 프로퍼티는 버튼이 실행되면 증가하고 @Published 프로퍼티는 모든 관찰자가 변경된 신호를 수신하도록 합니다.

변경 사항이 관찰자를 통해 어떻게 위임되는지 더 잘 이해하기 위해 더 이상 @Published 프로퍼티 래퍼를 사용하지 않도록 위의 코드를 수정할 수 있습니다. 대신 관찰 가능한 객체 게시자를 사용하고 변경 신호를 수동으로 보냅니다

final class CounterViewModel: ObservableObject {
    private(set) var count = 0

    func incrementCounter() {
        count += 1
        objectWillChange.send()
    }
}

증가 함수가 실행되면 보기가 계속 업데이트되는 것을 볼 수 있습니다. 대부분의 경우 objectWillChange.send() 메서드를 수동으로 호출할 필요가 없습니다. 그러나 게시된 여러 프로퍼티를 한 번에 업데이트하여 해당 프로퍼티를 일반 인수로 표시하고 대신 수동 신호를 사용하는 경우 더 나은 방법이 될 수 있습니다.

위의 예제는 SwiftUI 보기가 관찰된 객체 내부에서 감지된 변경 사항을 기반으로 다시 그리는 방법을 보여줍니다. 이제 @StateObject 프로퍼티 래퍼를 살펴보고 어떻게 다른지 살펴보겠습니다.

What is a @StateObject?

@StateObject 프로퍼티 래퍼는 @ObservedObject와 유사하게 작동 합니다. 대신 @StateObject 프로퍼티 래퍼를 사용하도록 이전 코드 예제를 변경할 수 있으며, 아무 것도 변경되지 않은 것 같습니다.

struct CounterView: View {
    /// Using @StateObject instead of @ObservedObject
    @StateObject var viewModel = CounterViewModel()

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

아무것도 바뀌지 않는다는 사실 떄문에 @ObservedObject를 자주 사용하기로 결정했습니다. 그러나 @ObservedObject 대신 @StateObject를 사용해야 할 떄를 명확히 하는 데에는 상당한 차이가 있습니다. @StateObject 프로퍼티 래퍼로 표시된 관찰된 객체는 포함하는 뷰 구조체가 다시 그려질 때 파괴되거나 다시 인스턴스화되지 않습니다. 다른 보기에 사용자의 보기가 포함된 경우 이 차이점을 이해하는 것이 필수적입니다.

이것이 어떻게 작동하는 보여주기 위해 이전에 사용된 카운터 뷰를 다른 뷰 안에 래핑할 수 있습니다.

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()
    }
}

랜덤 보기에는 랜덤 버튼을 눌러 랜덤숫자를 생성할 수 있습니다. @State 프로퍼티 래퍼로 표시된randomNumber 프로퍼티는 뷰를 다시 그려 CounterView가 재생성되도록 합니다.

새로운 랜덤숫자가 생성되면 CounterView 내에서 @ObservedObject를 사용하면 카운터가 재설정되는 것을 볼 수 있습니다.

@ObservedObject @StateObject

수정은 CounterView 보기 모델 프로퍼티를 @ObservedObject 대신 @StateObject로 변경하는 것 입니다.

위에서 설명된 것처럼 상태 객체는 뷰 모델이 다시 그리기 간에 유지되도록 하고 카운터 값이 동일하게 유지 되도록 합니다.

So, when should I use a @StateObject?

이제 둘 다 어떻게 작동하는지 알고 있지만 @ObservedObject 프로퍼티 래퍼 대신 @StateObject를 사용하는 경우를 더 긍정적으로 생각해도 좋습니다.

SwiftUI가 언제든지 뷰를 생성하거나 다시 생성할 수 있기 때문에 뷰 내부에 @ObserverdObject를 생성하는 것은 안전하지 않습니다. @ObservedObject를 종속성으로 주입하지 않는 한 뷰를 다시 그린 후 일관된 결과를 보장하기 위해 @StateObject 래퍼를 사용하는게 좋습니다.

Should I use @StateObject for all views using the same instance?

라인 아래에서 동일한 @StateObject 인스턴스를 관찰하는 형제는 이 프로퍼티 래퍼로 개체를 표시할 필요가 없습니다. 두 곳에서 개체의 수명 주기를 유지하고 관리하도록 요청할 것이므로 이렇게 하지 않는 것이 중요 합니다. 이전 섹션에서 설명한 것처럼 관찰된 개체를 주입하는 경우 @ObservedObject를 사용해야 합니다.

결국 차이점

@ObservedObject

@ObservedObject 결국 View에 종속되는 느낌. ObservableObject에서 데이터 변화가 있다면 View를 처음부터 다시 그려진다. 또한 View와 생명주기를 같이 하며, View가 사라지면 @ObservedObject의 인스턴스도 사라지고, View가 다시 그려지면 @ObservedObject 의 인스턴스도 사라졌다가 다시 생성된다.

@StateObject

@StateObject 반대로, View에 종속되지 않는다. 데이터의 변화가 있다면 View를 처음부터 다시 그리는 것이 아니라, 그 데이터를 사용하는 부분만 다시 그린다. View가 사라지든 다시 생기든 신경 안쓰고 알아서 있는 느낌. View와 생명주기가 따로 논다.

참고 링크
@StateObject vs. @ObservedObject: The differences explained

https://wondev.tistory.com/5

'iOS > SwiftUI' 카테고리의 다른 글

SwiftUI TextField  (0) 2023.11.10
SwiftUI ZStack 활용  (0) 2023.11.07
SwiftUI) NavigationBar 뒤로가기 버튼 안보이게 하기  (0) 2023.06.12
SwiftUI) List NavigationLink item ">" 숨기는 방법  (0) 2023.05.31
SwiftUI alert in iOS14  (0) 2023.01.17