@StateObject: ce este, diferența de @ObservedObject și exemple

Autor: IT Sectr Publicat: 2026-06-19 Timp de citire: 7 min

@StateObject este un Property Wrapper în SwiftUI pentru crearea și posesia unei instanțe ObservableObject direct în view. SwiftUI garantează că obiectul este inițializat o singură dată pe durata ciclului de viață al vizualizării și nu este recreat la re-renderizări. Conform Apple Developer Documentation (2025), @StateObject este recomandat pentru view-urile rădăcină care creează sursa de date. @StateObject este alegerea corectă pentru posesia ObservableObject în ierarhia SwiftUI.

Principalele puncte

  • @StateObject — Property Wrapper pentru crearea și posesia ObservableObject în view
  • Instanță unică — obiectul este creat o dată și nu este recreat la re-renderizări
  • Sursa adevărului — @StateObject garantează stabilitatea datelor pentru întreaga ierarhie
  • Diferența de @ObservedObject — @ObservedObject nu posedă obiectul și îl poate pierde
  • View-uri rădăcină — @StateObject este folosit în view-ul care creează obiectul

Ce este @StateObject în SwiftUI?

@StateObject este un Property Wrapper apărut în SwiftUI 2.0 (iOS 14) care combină capacitățile @ObservedObject și @State. La fel ca @ObservedObject, se abonează la modificările ObservableObject. Ca @State, garantează că datele supraviețuiesc inițializărilor repetate ale structurii view-ului. @StateObject creează obiectul o dată la prima apariție a view-ului pe ecran și îl stochează în heap-ul SwiftUI.

Înainte de apariția @StateObject, dezvoltatorii foloseau @ObservedObject pentru toate ObservableObject-urile, inclusiv cele create în view-uri. Aceasta ducea la pierderea frecventă a datelor la actualizarea view-ului părinte, când structura view-ului era recreată, luând cu ea și instanța @ObservedObject. @StateObject a rezolvat această problemă adăugând garanția stabilității.

Regula de bază: @StateObject se aplică în view-ul care creează obiectul în inițializatorul implicit (let model = ViewModel()). View-urile copil care primesc acest obiect folosesc @ObservedObject. Această separare garantează o singură sursă a adevărului în întreaga ierarhie.

Ciclul de viață al @StateObject

SwiftUI gestionează ciclul de viață al @StateObject printr-un manager de stocare similar cu @State. La prima apariție a view-ului, SwiftUI alocă memorie pentru obiect și îl salvează într-o zonă persistentă. La re-renderizări (apelul body), obiectul nu este recreat — se folosește instanța existentă. Obiectul trăiește cât timp view-ul se află în ierarhie.

Când view-ul este eliminat din ierarhie, SwiftUI distruge @StateObject, apelând deinit. La re-adaugarea view-ului în ierarhie, se creează o nouă instanță. Acest lucru este important de luat în considerare la proiectare: dacă trebuie să păstrați date între eliminările view-ului, utilizați un strat de servicii (singleton sau DI) sau @AppStorage pentru persistență.

swift
class TimerViewModel: ObservableObject {
    @Published var seconds: Int = 0
    private var timer: Timer?

    func start() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            self.seconds += 1
        }
    }

    deinit {
        timer?.invalidate()
    }
}

struct TimerView: View {
    @StateObject var viewModel = TimerViewModel()

    var body: some View {
        Text("\(viewModel.seconds)s")
            .onAppear { viewModel.start() }
    }
}

În exemplu, TimerViewModel este creat prin @StateObject și trăiește cât timp TimerView este pe ecran. Timer-ul pornește în onAppear și se oprește în deinit. Dacă s-ar fi folosit @ObservedObject, la fiecare re-renderizare a TimerView s-ar fi creat un nou TimerViewModel cu secundele = 0, iar timer-ul nu ar fi funcționat niciodată corect. @StateObject garantează că viewModel este unic și stabil.

@StateObject vs @ObservedObject: comparație

Alegerea între @StateObject și @ObservedObject depinde de cine posedă obiectul. Dacă view-ul creează obiectul — @StateObject. Dacă view-ul primește obiectul gata făcut — @ObservedObject. Această regulă este atât de importantă încât Xcode emite un avertisment la utilizarea @StateObject într-un view copil care primește obiectul printr-un inițializator.

SituațiaWrapper recomandat
View-ul creează modelul prin ViewModel()@StateObject
View-ul primește modelul de la părinte@ObservedObject
Modelul este folosit într-un singur view@StateObject
Modelul este transmis prin Environment@EnvironmentObject
Modelul este necesar pentru previzualizare@ObservedObject + mock

În practică, la începutul proiectului se folosește adesea @StateObject în view-ul rădăcină și @ObservedObject în toate view-urile copil. Pe măsură ce aplicația crește, o parte din @StateObject poate fi înlocuită cu @EnvironmentObject pentru simplificarea ierarhiei. Cu toate acestea, @StateObject rămâne cea mai bună alegere pentru ecrane modulare cu logică proprie.

Pattern-uri de utilizare @StateObject

Primul pattern — MVVM cu @StateObject. ViewModel ca ObservableObject este creat în view prin @StateObject. ViewModel conține proprietăți @Published și logică de business. View-ul se abonează la modificări și actualizează interfața. Această abordare oferă izolare testabilă: ViewModel poate fi testat fără UI, creând o instanță direct.

Al doilea pattern — @StateObject cu dependențe. Dacă ViewModel necesită servicii, utilizați inițializarea cu parametri. De exemplu, @StateObject var viewModel = UserViewModel(api: APIClient.shared). Totuși, fiți atenți: parametrii se calculează la fiecare re-renderizare a body-ului, dar obiectul este creat o singură dată. SwiftUI ignoră inițializările ulterioare ale @StateObject.

Al treilea pattern — @StateObject imbricate. În SwiftUI se pot avea mai multe @StateObject într-un singur view, dar acest lucru este rareori justificat. De obicei, un @StateObject răspunde pentru întregul set de date al view-ului. Dacă logica devine prea complexă, împărțiți-o într-o compoziție de servicii @ObservedObject în cadrul unui singur @StateObject.

swift
struct AppView: View {
    @StateObject var router = NavigationRouter()
    @StateObject var auth = AuthViewModel()

    var body: some View {
        ContentView()
            .environmentObject(router)
            .environmentObject(auth)
    }
}

În exemplu, AppView creează două @StateObject: NavigationRouter pentru gestionarea navigării și AuthViewModel pentru autentificare. Ambele obiecte sunt injectate în Environment prin environmentObject. Orice view copil poate accesa aceste obiecte prin @EnvironmentObject fără a le transmite prin lanțul de inițializatori.

@StateObject și inițializarea cu parametri

@StateObject suportă inițializarea cu orice parametri, dar cu o particularitate importantă: inițializatorul este apelat o singură dată. La re-renderizările body-ului, noua valoare a parametrilor este ignorată. Aceasta înseamnă că dacă transmiteți @State var id: Int = 5 către @StateObject var vm = ViewModel(id: id), la modificarea id-ului, ViewModel nu va primi noua valoare.

Pentru a rezolva această problemă, utilizați onReceive sau onAppear pentru sincronizare. Abonați-vă la modificările parametrului în ViewModel prin Combine sau transmiteți parametrii prin metoda .onChange(of:) la nivelul view-ului. Alternativă — utilizați @ObservedObject în loc de @StateObject dacă obiectul trebuie să reacționeze dinamic la modificări externe.

swift
struct DetailView: View {
    let itemId: Int
    @StateObject var viewModel = DetailViewModel()

    var body: some View {
        Text(viewModel.title)
            .onAppear { viewModel.load(id: itemId) }
    }
}

Abordarea corectă: DetailView primește itemId ca proprietate let (transmisă prin inițializatorul structurii), iar @StateObject creează DetailViewModel fără parametri. În onAppear se apelează metoda load(id:), care încarcă datele pentru ID-ul transmis. Aceasta garantează că ViewModel este creat prin mecanismul @StateObject, dar datele sunt încărcate la fiecare apariție a view-ului cu ID-ul curent.

Greșeli tipice cu @StateObject

Greșeala principală — utilizarea @StateObject în view-urile copil care primesc obiectul de la părinte. Dacă ParentView creează @StateObject model, iar ChildView declară @StateObject var model: ModelType (cu parametru implicit), atunci ChildView va crea propria instanță independentă. Obiectele părintelui și copilului nu vor fi legate, iar modificările într-unul nu se vor reflecta în celălalt.

A doua greșeală — plasarea @StateObject în List sau ForEach. Fiecare element al listei își creează propriul @StateObject, ceea ce duce la multiple instanțe independente. Pentru liste, este corect să transmiteți un singur ObservableObject tuturor elementelor prin @ObservedObject sau să utilizați structuri Identifiable cu @State în interiorul List.

A treia problemă — lipsa curățării în deinit. @StateObject trăiește pe tot ciclul de viață al view-ului. Dacă obiectul creează timer-e, abonamente Combine sau cereri de rețea, deinit trebuie să le anuleze. Altfel, scurgeri de memorie și continuarea lucrului în fundal după închiderea ecranului sunt inevitabile. Folosiți întotdeauna Cancellable store Combine sau invalidate timer-elor în deinit.

Întrebări frecvente

Când a apărut @StateObject în SwiftUI?

@StateObject a fost adăugat în SwiftUI 2.0 la WWDC 2020 împreună cu iOS 14, macOS 11, watchOS 7 și tvOS 14. Înainte de aceasta, @ObservedObject era singura modalitate de lucru cu ObservableObject, ceea ce ducea la bug-uri frecvente de pierdere a datelor.

Poate @StateObject să fie opțional?

Nu, @StateObject nu suportă tipurile Optional. Obiectul trebuie inițializat la declarare. Dacă aveți nevoie de un obiect opțional, utilizați @ObservedObject sau @EnvironmentObject cu tip opțional.

Cum verific că @StateObject este creat o singură dată?

Adăugați print(#function) în inițializatorul și deinit-ul ObservableObject-ului. Dacă la re-renderizări init nu este apelat — @StateObject funcționează corect. Dacă init este apelat de fiecare dată — înlocuiți @ObservedObject cu @StateObject.

Pot folosi @StateObject cu UIKit prin UIHostingController?

Da, @StateObject funcționează în view-uri SwiftUI încorporate în UIKit prin UIHostingController. Ciclul de viață al obiectului este legat de view-ul SwiftUI, nu de UIViewController. Dacă view-ul SwiftUI este înlocuit, @StateObject este distrus.

Ce este mai bine: un @StateObject cu ViewModel mare sau mai multe mici?

Mai multe @StateObject mici cu responsabilități separate. Aceasta îmbunătățește testabilitatea, reutilizarea și performanța — la modificarea unui obiect, doar părțile abonate ale interfeței se redesenează, nu întregul view.

Concluzii

  • @StateObject — Property Wrapper pentru crearea și posesia ObservableObject în view
  • Instanță unică — obiectul nu este recreat la re-renderizările body-ului
  • Sursa adevărului — @StateObject în view-ul rădăcină garantează stabilitatea datelor pentru ierarhie
  • Regula de alegere — @StateObject pentru creare, @ObservedObject pentru primirea obiectului gata făcut
  • Inițializare — parametrii în @StateObject se calculează o dată, actualizările nu sunt urmărite
  • Deinit — curățarea obligatorie a timer-elor și abonamentelor în deinit-ul ObservableObject
  • iOS 14+ — @StateObject disponibil începând cu iOS 14, macOS 11, watchOS 7, tvOS 14

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și