Swift

어서와 @ObservedObject 처음이지?

Thor_yeom 2023. 1. 25. 18:23

@ObservaedObect 너란 녀석... 정복하고 싶다

 

@ObservaedObect를 서치하다 보면 @StateObject라는것이 같이 보일 것이다. 비교해가면서 알아보자 

 

일단 @ObservaedObect가 무엇인가? 

공식문서

공식문서에서 찾아보면 관찰가능한 객체를 구독하고 변경될때 마다 view를 무효화하는 property wrapper라고 되어있습니다. 쉽게 풀이해보자면  관찰중인 객체의 변경에 반응해서 화면을 업데이트해준다 라고 생각하면 됩니다.

 

관찰중인 객체에 반응해서 화면을 업데이트한다??... 전에 배웠던 @State랑 비슷한 느낌적인 느낌인데... 네 맞습니다. 

@State에서는 특정 view에서만 사용되는 프로퍼티 였다면 ObservaedObect는 다양한하게 사용할수 있습니다.

 

여기서 ObservaedObect의 특징을 살펴보면

  • 복잡한 프로퍼티 ( 여러 프로퍼티나 메소드가 있거나, 여러 view에서 공유할 수 있는 타입) 대신 @ObservaedObect를 사용 
  • String이나 interger같은 간단한 프로퍼티 대신 외부 참조 타입을 사용한다는점 제외하면 @State와 유사
  • @ObservaedObect를 사용하려면 ObservableObect 프로토콜을 채택해야함  --> ObservableObect는 클래스 전용 프로토콜
  • 변경되었다는 것을 알리기 위한 방법으로 @Published 프로퍼티 사용하면 됨 --> SwiftUI에서 view reload 트리거 역할

이것만은 꼭 기억하자!  관찰중인 객체의 변경에 반응해서 화면을 업데이트해준다

 

코드를 보면

//ObservableObject은 클래스 전용 프로토콜
class CounterViewModel: ObservableObject {
    // 값이 변경되는 것을 즉각적으로 view에 알려줌
    @Published var count: Int = 0
    
    // count를 +1씩 올려주는 메소드
    func incrementCounter() {
        count += 1
    }
}

struct CounterView: View {
    //외부 클래스를 가져옴 count 변수 활용 할 수 있음
    @ObservedObject var counterViewModel: CounterViewModel = CounterViewModel()
    var body: some View {
        VStack {
            Text("\(counterViewModel.count)")
            Button {
                counterViewModel.incrementCounter()
            } label: {
                Text("1씩 증가")
            }

        }
    }
}

뷰 모델 내부의 count값은 버튼을 누를때 마다 증가하게 되고, @Published가 변화했다는 신호를 view로 보내서 변화를 알게 됨. @State랑 비슷한 부분이 있는데... 굳이 @ObservaedObect를 쓰는 이유가 뭔가요...  간단한 예시를 들어서 그렇지만 데이터를 받아오거나 하면 사이즈가 크기때문에 그렇단다...

 

또한 ObservableObect를 채택하면 프로토콜안에 objectWillChange.send()를 사용할 수 있지만 수동으로 쏘는 경우는 거의 없지만, 필요에 의해서는 사용될수 있음.

 

여기 까지는 이해가 되었죠? 그러면 당최 @StateObject랑은 뭐가 다른건가요??? 

ContentView를 만들어서 뷰를 완성시켜보겠습니다.

import SwiftUI

//ObservableObject은 클래스 전용 프로토콜
class CounterViewModel: ObservableObject {
    // 값이 변경되는 것을 즉각적으로 view에 알려줌
    @Published var count: Int = 0
    
    // count를 +1씩 올려주는 메소드
    func incrementCounter() {
        count += 1
    }
}

struct CounterView: View {
    //외부 클래스를 가져옴 count 변수 활용 할 수 있음
    @ObservedObject var counterViewModel: CounterViewModel = CounterViewModel()
    var body: some View {
        VStack {
            Text("\(counterViewModel.count)")
            Button {
                counterViewModel.incrementCounter()
            } label: {
                Text("1씩 증가")
            }

        }
    }
}


struct ContentView: View {
    @State private var randomInt: Int = 0
    var body: some View {
        VStack {
            Text("\(randomInt)")
            Button {
                randomInt = (0..<1000).randomElement()!
            } label: {
                Text("random Number")
            }
            CounterView()

        }
    }
}



struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

 

동영상을 보면 random 버튼을 누를때 마다 숫자가 0으로 초기화 되는 것을 볼수 있습니다. @ObservaedObect의 정의가 뭐라고 했죠? 네 바로 관찰하는 객체가 변하면 화면을 다시 그린다는 겁니다. 코드를 보면

struct ContentView: View {
    @State private var randomInt: Int = 0
    var body: some View {
        VStack {
            Text("\(randomInt)")
            Button {
                randomInt = (0..<1000).randomElement()!
            } label: {
                Text("random Number")
            }
            CounterView()

        }
    }
}

상위뷰의 ContentView에서 @State로 randomInt 값이 변하므로 화면이 다시 랜더링 될테고 그 하위에 있는 CounterView() 또한 초기화 되는 것입니다... 응?? 이게 이렇게 되는거야?

 

그렇게 해서 나온것이 @StateObject입니다. 적용 방법은 쉽습니다.  @ObservaedObect 대신에 @StateObject을 적으면 됩니다. 참 쉽죠? 

struct CounterView: View {
    //@ObservedObject --> StateObject 변경
    @StateObject var counterViewModel: CounterViewModel = CounterViewModel()
    var body: some View {
        VStack {
            Text("\(counterViewModel.count)")
            Button {
                counterViewModel.incrementCounter()
            } label: {
                Text("1씩 증가")
            }

        }
    }
}

 

@StateObjcect 적용

 

어떻게 이런 일이 가능한거죠??

공식문서 StateObject

공식문서 보면 관찰가능한 객체 생성해주는 프로퍼티 래퍼 라고 적혀있습니다. 여기서 가장 중요한 객체 생성이라는 단어 인데, 해당 클래스를 새로운 객체로 생성하고 -> 참조해서 -> 별도의 객체로서 관리한다는 겁니다. 그렇게 되면 화면이 랜더링 되어도 참조 되어있기 때문에 값이 초기화 되지 않는다. 두둥...!

 

즉, ContentView와 별개의 메모리 공간에 저장해 데이터를 안전하게 보관 할 수 있다.

 

결론 : 

@ObservaedObect  @StateObject 를 사용하는것은 상황에 따라 달라지는데 @StateObject를 사용하면 생성하는 화면에서도 일관된 결과를 보장할 수 있고, 단지 의존관계로 주입하는 거라면 @ObservaedObect를 사용할 수 있다. 

 

 

https://pilgwon.github.io/post/state-object-vs-observed-object

 

[수위프트UI/번역] @StateObject와 @ObservedObject, 무엇이 다를까요?

@StateObject vs. @ObservedObject: The differences explained

pilgwon.github.io

https://nsios.tistory.com/120

 

[SwiftUI] Published, ObservableObject

SwiftUI를 공부하다보면 @Published 라는 키워드를 보게될거에요 이와 같이 언급되는게 ObservableObject죠 ObservableObject는 필수구현을 필요로 하지않는 프로토콜이에요 Combine에 속한 기능이구요 클래스

nsios.tistory.com

https://medium.com/hcleedev/swift-observedobject%EC%99%80-stateobject-4f851ed9ef0d

 

Swift: ObservedObject와 StateObject

SwiftUI의 ObservedObject와 StateObject가 무엇인지, 두 프로퍼티 래퍼에는 어떤 차이가 있는지 알아보자

medium.com

 

'Swift' 카테고리의 다른 글

우당탕탕 앱 출시 리젝...  (0) 2023.02.11
우당탕탕 팀 프로젝트 앱출시  (0) 2023.02.09
어서와 @Binding은 처음이지?  (0) 2023.01.25
어서와 @State는 처음이지?  (2) 2023.01.25
어서와 @FocusState는 처음이지?  (0) 2022.12.27