Si j'ai un ObservableObject
dans SwiftUI, je peux y faire référence en tant que @ObservedObject
:
class ViewModel: ObservableObject { @Published var someText = "Hello World!" } struct ContentView: View { @StateObject var viewModel = ViewModel() var body: some View { Text(viewModel.someText) } }
Ou en tant que @StateObject
:
class ViewModel: ObservableObject { @Published var someText = "Hello World!" } struct ContentView: View { @ObservedObject var viewModel = ViewModel() var body: some View { Text(viewModel.someText) } }
Mais quelle est la différence réelle entre les deux? Y a-t-il des situations où l'un est meilleur que l'autre, ou ce sont deux choses complètement différentes?
6 Réponses :
@ObservéObjet
Lorsqu'une vue crée sa propre instance @ObservedObject
, elle est recréée chaque fois qu'une vue est supprimée et redessinée:
struct ContentView: View { @StateObject var viewModel = ViewModel() }
Au contraire, une variable @State
conservera sa valeur lorsqu'une vue est redessinée.
@StateObject
Un @StateObject
est une combinaison de @ObservedObject
et @State
- l'instance du ViewModel
sera conservée et réutilisée même après qu'une vue est supprimée et redessinée:
struct ContentView: View { @ObservedObject var viewModel = ViewModel() }
Performance
Bien qu'un @ObservedObject
puisse avoir un impact sur les performances si la vue est forcée de recréer souvent un objet lourd, cela n'a pas beaucoup d'importance lorsque @ObservedObject
n'est pas complexe.
Quand utiliser @ObservedObject
Il peut sembler qu'il n'y a aucune raison d'utiliser un @ObserverObject
, alors quand doit-il être utilisé?
Vous devez utiliser @StateObject pour toutes les propriétés observables que vous initialisez dans la vue qui l'utilise. Si l'instance ObservableObject est créée en externe et transmise à la vue qui l'utilise, marquez votre propriété avec @ObservedObject.
Notez qu'il y a trop de cas d'utilisation possibles et qu'il peut parfois être souhaitable de recréer une propriété observable dans votre vue. Dans ce cas, il est préférable d'utiliser un @ObservedObject
.
Liens utiles:
Il n'est pas clair pour moi s'il existe un cas d'utilisation d'ObservedObject, pourquoi n'est-il pas obsolète? Le cas d'utilisation de @DavidPasztor montre qu'ils peuvent être équivalents, mais quand ObservedObject serait-il le premier choix?
@RyanHeitner Tout est dans les liens utiles que j'ai fournis. Mais comme il est vrai que tout le monde ne les lira pas tous, j'ai ajouté le résumé à ma réponse.
Est-il possible qu'une vue ait les deux?
@AbhijitSarkar Oui, ces wrappers de propriété sont appliqués aux classes ObservableObject, pas à la vue elle-même. Vous pouvez avoir à la fois les propriétés StateObject et ObservedObject dans votre vue.
Même si la réponse de pawello2222 a bien expliqué les différences lorsque la vue elle-même crée son modèle de vue, il est important de noter les différences lorsque le modèle de vue est injecté dans la vue.
Lorsque vous injectez le modèle de vue dans la vue, tant que le modèle de vue est un type de référence, il n'y a aucune différence entre @ObservedObject
et @StateObject
, car l'objet qui a injecté le modèle de vue dans votre vue doit contenir une référence pour voir le modèle ainsi, le modèle de vue n'est pas détruit lorsque la vue enfant est redessinée.
class ViewModel: ObservableObject {} struct ParentView: View { @ObservedObject var viewModel = ViewModel() var body: some View { ChildView(viewModel: viewModel) // You inject the view model into the child view } } // Even if `ChildView` is discarded/redrawn, `ViewModel` is kept in memory, since `ParentView` still holds a reference to it - `ViewModel` is only released and hence destroyed when `ParentView` is destroyed/redrawn. struct ChildView: View { @ObservedObject var viewModel: ViewModel }
La documentation Apple a expliqué pourquoi l'initialisation avec ObservedObject
n'est pas sûre .
SwiftUI peut créer ou recréer une vue à tout moment, il est donc important que l'initialisation d'une vue avec un ensemble donné d'entrées aboutisse toujours à la même vue. Par conséquent, il n'est pas sûr de créer un objet observé dans une vue.
La solution est StateObject
.
Dans le même temps, la documentation nous a montré comment créer des modèles de données dans une vue (ou une application / scène) lorsqu'elle peut conserver la vérité et la transmettre à une autre vue.
struct LibraryView: View { @StateObject var book = Book() // Hold on to the 1 truth var body: some View { BookView(book: book) // Pass it to another view } } struct BookView: View { @ObservedObject var book: Book // From external source }
Auriez-vous besoin d'ObservedObject puisqu'un type de référence est passé dans BookView? J'essaie de savoir quand vous auriez besoin d'ObservedObject.
Oh, je pense que j'ai compris. Sans ObservedObject, vous n'obtiendrez aucune modification poussée vers BookView qui se produirait pour réserver si le parent (ou d'autres sources) apportent des modifications au livre StateObject.
Voici un exemple. Chaque fois que vous cliquez sur le bouton d' refresh
, CountViewObserved
force StateObjectClass à être détruit / recréé, afin que vous puissiez voir 0
, ce qui n'est pas attendu.
import SwiftUI import Combine class StateObjectClass:ObservableObject{ let type:String let id:Int @Published var count = 0 init(type:String){ self.type = type self.id = Int.random(in: 0...1000) print("type:\(type) id:\(id) init") } deinit { print("type:\(type) id:\(id) deinit") } } struct CountViewState:View{ @StateObject var state = StateObjectClass(type:"StateObject") var body: some View{ VStack{ Text("@StateObject count :\(state.count)") Button("+1"){ state.count += 1 } } } } struct CountViewObserved:View{ @ObservedObject var state = StateObjectClass(type:"Observed") var body: some View{ VStack{ Text("@Observed count :\(state.count)") Button("+1"){ state.count += 1 } } } } struct ContentView: View { @State var count = 0 var body: some View { VStack{ Text("refresh CounterView count :\(count)") Button("refresh"){ count += 1 } CountViewState() .padding() CountViewObserved() .padding() } } }
@StateObject
est un état d'une vue donnée, donc son instance est conservée par SwiftUI à travers body
mises à jour du body
. Il n'est cependant pas conservé lors de l'exécution en Aperçu.
@ObservedObject
en revanche, n'est qu'un objet observé par une View
donnée, et n'est donc pas retenu par SwiftUI (il doit être conservé en dehors de la View
).
En d'autres termes - il semble que SwiftUI conserve une référence strong
de @StateObject
et unowned
référence sans @ObservedObject
de @ObservedObject
.
Source conservée vs source non conservée, source de comportement des aperçus , vers 8h30 environ.
La différence entre disons:
@ObservedObject var livre: BookModel et @StateObject var livre: BookModel
@ObservedObject ne possède PAS le "livre" d'instance, il est de votre responsabilité de gérer le cycle de vie de l'instance.
Mais lorsque vous voulez lier le cycle de vie de votre «livre» observableObject à votre vue comme dans @State, vous pouvez utiliser @StateObject. Dans ce cas, SwiftUI possédera l'observableObject et la création et la destruction seront liées au cycle de vie de la vue SwiftUI maintiendra l'objet en vie pendant tout le cycle de vie de la vue Ceci est idéal pour les ressources coûteuses, vous n'avez pas besoin de jouer avec onDisappear plus pour libérer des ressources
Cette clarification est tirée de l'essentiel des données WWDC2020 dans SwiftUI:
hackingwithswift.com/quick-start/swiftui/...