@StateObject: ano ito, pagkakaiba sa @ObservedObject at mga halimbawa

May-akda: IT Sectr Nai-publish: 2026-06-19 Oras ng pagbabasa: 7 min

Ang @StateObject ay isang Property Wrapper sa SwiftUI para sa paglikha at pagmamay-ari ng instance ng ObservableObject nang direkta sa view. Ginagarantiya ng SwiftUI na ang object ay ini-initialize nang isang beses sa buong lifecycle ng view at hindi muling nililikha sa paulit-ulit na pag-render. Ayon sa Apple Developer Documentation (2025), ang @StateObject ay inirerekomenda para sa mga root view na lumilikha ng source ng data. @StateObject ang tamang pagpili para sa pagmamay-ari ng ObservableObject sa hierarchy ng SwiftUI.

Mga Pangunahing Punto

  • @StateObject — Property Wrapper para sa paglikha at pagmamay-ari ng ObservableObject sa view
  • Isang instance — ang object ay nilikha nang isang beses at hindi muling nililikha sa pag-render
  • Pinagmulan ng katotohanan — ginagarantiya ng @StateObject ang katatagan ng data para sa buong hierarchy
  • Pagkakaiba sa @ObservedObject — hindi pagmamay-ari ng @ObservedObject ang object at maaaring mawala ito
  • Mga root view — ginagamit ang @StateObject sa view na lumilikha ng object

Ano ang @StateObject sa SwiftUI?

@StateObject ay isang Property Wrapper na lumitaw sa SwiftUI 2.0 (iOS 14) na pinagsasama ang mga kakayahan ng @ObservedObject at @State. Tulad ng @ObservedObject, nag-subscribe ito sa mga pagbabago ng ObservableObject. Tulad ng @State, ginagarantiya nito na ang data ay makakaligtas sa paulit-ulit na pagsisimula ng structure ng view. Nililikha ng @StateObject ang object nang isang beses sa unang paglitaw ng view sa screen at iniimbak ito sa heap ng SwiftUI.

Bago ang paglitaw ng @StateObject, ginamit ng mga developer ang @ObservedObject para sa lahat ng ObservableObject, kasama ang mga nilikha sa mga view. Ito ay humantong sa madalas na pagkawala ng data kapag na-update ang parent view, kapag ang structure ng view ay muling nilikha, dala ang @ObservedObject instance. Nalutas ng @StateObject ang problemang ito sa pamamagitan ng pagdaragdag ng garantiya ng katatagan.

Ang pangunahing patakaran: Ang @StateObject ay inilalapat sa view na lumilikha ng object sa default na initializer (let model = ViewModel()). Ang mga child view na tumatanggap ng object na ito ay gumagamit ng @ObservedObject. Ang ganitong paghihiwalay ay ginagarantiya ang nag-iisang pinagmulan ng katotohanan sa buong hierarchy.

Lifecycle ng @StateObject

Pinamamahalaan ng SwiftUI ang lifecycle ng @StateObject sa pamamagitan ng storage manager, katulad ng @State. Sa unang paglitaw ng view, naglaan ang SwiftUI ng memory para sa object at ini-save ito sa persistent area. Sa paulit-ulit na pag-render (tawag sa body), hindi muling nililikha ang object — ginagamit ang umiiral na instance. Nabubuhay ang object hangga't ang view ay nasa hierarchy.

Kapag ang view ay tinanggal mula sa hierarchy, sinisira ng SwiftUI ang @StateObject, na tumatawag sa deinit. Kapag idinagdag muli ang view sa hierarchy, isang bagong instance ang nilikha. Ito ay mahalagang isaalang-alang sa pagdisenyo: kung kailangan mong panatilihin ang data sa pagitan ng mga pagtanggal ng view, gumamit ng service layer (singleton o DI) o @AppStorage para sa pagpapatuloy.

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

Sa halimbawa, ang TimerViewModel ay nilikha sa pamamagitan ng @StateObject at nabubuhay hangga't ang TimerView ay nasa screen. Ang timer ay nagsisimula sa onAppear at humihinto sa deinit. Kung ginamit ang @ObservedObject, sa bawat pag-render ng TimerView ay lilikha ng bagong TimerViewModel na may mga segundo = 0 at hindi kailanman gagana nang tama ang timer. Ginagarantiya ng @StateObject na ang viewModel ay nag-iisa at matatag.

@StateObject vs @ObservedObject: paghahambing

Ang pagpili sa pagitan ng @StateObject at @ObservedObject ay depende sa kung sino ang nagmamay-ari ng object. Kung ang view ay lumilikha ng object — @StateObject. Kung ang view ay tumatanggap ng handa nang object — @ObservedObject. Ang patakarang ito ay napakahalaga kaya naglalabas ang Xcode ng babala kapag ginagamit ang @StateObject sa isang child view na tumatanggap ng object sa pamamagitan ng initializer.

SitwasyonInirerekomendang wrapper
View lumilikha ng modelo sa pamamagitan ng ViewModel()@StateObject
View tumatanggap ng modelo mula sa magulang@ObservedObject
Modelo ay ginagamit sa isang view@StateObject
Modelo ay ipinapasa sa pamamagitan ng Environment@EnvironmentObject
Modelo ay kailangan para sa preview@ObservedObject + mock

Sa praktika, sa simula ng proyekto madalas gamitin ang @StateObject sa root view at @ObservedObject sa lahat ng child view. Habang lumalaki ang application, ang bahagi ng @StateObject ay maaaring palitan ng @EnvironmentObject upang pasimplehin ang hierarchy. Gayunpaman, ang @StateObject ay nananatiling pinakamahusay na pagpili para sa modular na mga screen na may sariling lohika.

Mga pattern ng paggamit ng @StateObject

Ang unang pattern — MVVM gamit ang @StateObject. Ang ViewModel bilang ObservableObject ay nilikha sa view sa pamamagitan ng @StateObject. Ang ViewModel ay naglalaman ng @Published na mga property at lohika ng negosyo. Ang view ay nag-subscribe sa mga pagbabago at nag-a-update ng interface. Ang ganitong approach ay nagbibigay ng testable isolation: ang ViewModel ay maaaring subukan nang walang UI sa pamamagitan ng direktang paglikha ng instance.

Ang pangalawang pattern — @StateObject na may dependencies. Kung ang ViewModel ay nangangailangan ng mga serbisyo, gamitin ang pagsisimula gamit ang mga parameter. Halimbawa, @StateObject var viewModel = UserViewModel(api: APIClient.shared). Gayunpaman mag-ingat: ang mga parameter ay kinakalkula sa bawat pag-render ng body, ngunit ang object ay nilikha nang isang beses lamang. Hindi pinapansin ng SwiftUI ang mga sumusunod na pagsisimula ng @StateObject.

Ang pangatlong pattern — mga nested @StateObject. Sa SwiftUI, maaari kang magkaroon ng maraming @StateObject sa isang view, ngunit ito ay bihirang makatwiran. Karaniwan, isang @StateObject ang responsable para sa buong set ng data ng view. Kung ang lohika ay nagiging masyadong kumplikado, hatiin ito sa isang komposisyon ng mga serbisyo ng @ObservedObject sa loob ng isang @StateObject.

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

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

Sa halimbawa, ang AppView ay lumilikha ng dalawang @StateObject: NavigationRouter para sa pamamahala ng nabigasyon at AuthViewModel para sa pagpapatotoo. Ang parehong mga object ay ini-inject sa Environment sa pamamagitan ng environmentObject. Ang anumang child view ay maaaring ma-access ang mga ito sa pamamagitan ng @EnvironmentObject nang hindi dumadaan sa chain ng mga initializer.

@StateObject at pagsisimula gamit ang mga parameter

@StateObject ay sumusuporta sa pagsisimula gamit ang anumang mga parameter, ngunit may mahalagang katangian: ang initializer ay tinatawag nang isang beses lamang. Sa paulit-ulit na pag-render ng body, ang bagong halaga ng mga parameter ay binabalewala. Nangangahulugan ito na kung ipapasa mo ang @State var id: Int = 5 sa @StateObject var vm = ViewModel(id: id), kapag nagbago ang id, hindi makakatanggap ng bagong halaga ang ViewModel.

Upang malutas ang problemang ito, gamitin ang onReceive o onAppear para sa pag-sync. Mag-subscribe sa mga pagbabago ng parameter sa loob ng ViewModel sa pamamagitan ng Combine o ipasa ang mga parameter sa pamamagitan ng .onChange(of:) method sa antas ng view. Alternatibo — gamitin ang @ObservedObject sa halip na @StateObject kung ang object ay dapat dynamicong tumugon sa mga panlabas na pagbabago.

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

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

Ang tamang approach: Ang DetailView ay tumatanggap ng itemId bilang let property (ipinapasa sa pamamagitan ng initializer ng structure), at ang @StateObject ay lumilikha ng DetailViewModel nang walang parameter. Sa onAppear, ang method na load(id:) ay tinatawag, na naglo-load ng data para sa ipinasa na ID. Ginagarantiya nito na ang ViewModel ay nilikha ng mekanismo ng @StateObject, ngunit ang data ay nilo-load sa bawat paglitaw ng view gamit ang kasalukuyang ID.

Mga karaniwang pagkakamali sa @StateObject

Ang pangunahing pagkakamali — paggamit ng @StateObject sa mga child view na tumatanggap ng object mula sa magulang. Kung ang ParentView ay lumilikha ng @StateObject model, at ang ChildView ay nagdedeklara ng @StateObject var model: ModelType (na may default na parameter), ang ChildView ay lilikha ng sarili nitong independiyenteng instance. Ang mga object ng magulang at anak ay hindi magkakaugnay, at ang mga pagbabago sa isa ay hindi makikita sa isa.

Ang pangalawang pagkakamali — paglalagay ng @StateObject sa List o ForEach. Ang bawat elemento ng listahan ay lumilikha ng sarili nitong @StateObject, na humahantong sa maraming independiyenteng instance. Para sa mga listahan, ang tama ay magpasa ng isang ObservableObject sa lahat ng elemento sa pamamagitan ng @ObservedObject o gumamit ng Identifiable structures na may @State sa loob ng List.

Ang pangatlong problema — kawalan ng paglilinis sa deinit. Ang @StateObject ay nabubuhay sa buong lifecycle ng view. Kung ang object ay lumilikha ng mga timer, Combine subscription, o network request, dapat kanselahin sila ng deinit. Kung hindi, ang pagtagas ng memory at pagpapatuloy ng background work pagkatapos isara ang screen ay hindi maiiwasan. Palaging gumamit ng Combine Cancellable store o invalidate ng mga timer sa deinit.

Mga Madalas Itanong

Kailan lumitaw ang @StateObject sa SwiftUI?

@StateObject ay idinagdag sa SwiftUI 2.0 sa WWDC 2020 kasama ng iOS 14, macOS 11, watchOS 7, at tvOS 14. Bago ito, ang @ObservedObject ay ang tanging paraan upang magtrabaho gamit ang ObservableObject, na humantong sa madalas na bug sa pagkawala ng data.

Maaari bang maging opsiyonal ang @StateObject?

Hindi, @StateObject ay hindi sumusuporta sa mga uri ng Optional. Ang object ay dapat na mai-initialize sa deklarasyon. Kung kailangan mo ng isang opsiyonal na object, gamitin ang @ObservedObject o @EnvironmentObject na may opsiyonal na uri.

Paano suriin na ang @StateObject ay nilikha nang isang beses lamang?

Magdagdag ng print(#function) sa initializer at deinit ng ObservableObject. Kung ang init ay hindi tinatawag sa paulit-ulit na pag-render — gumagana nang tama ang @StateObject. Kung ang init ay tinatawag sa bawat pagkakataon — palitan ang @ObservedObject ng @StateObject.

Maaari ko bang gamitin ang @StateObject sa UIKit sa pamamagitan ng UIHostingController?

Oo, ang @StateObject ay gumagana sa mga SwiftUI view na naka-embed sa UIKit sa pamamagitan ng UIHostingController. Ang lifecycle ng object ay nakatali sa SwiftUI view, hindi sa UIViewController. Kung ang SwiftUI view ay pinalitan, ang @StateObject ay nawasak.

Alin ang mas mahusay: isang @StateObject na may malaking ViewModel o maraming maliliit?

Maraming maliliit na @StateObject na may hiwalay na mga responsibilidad. Pinapabuti nito ang testability, reusability, at performance — kapag nagbago ang isang object, ang mga naka-subscribe na bahagi lamang ng interface ang muling iginuguhit, hindi ang buong view.

Buod

  • @StateObject — Property Wrapper para sa paglikha at pagmamay-ari ng ObservableObject sa view
  • Isang instance — ang object ay hindi muling nililikha sa paulit-ulit na pag-render ng body
  • Pinagmulan ng katotohanan — @StateObject sa root view ay ginagarantiya ang katatagan ng data para sa hierarchy
  • Patakaran sa pagpili — @StateObject para sa paglikha, @ObservedObject para sa pagtanggap ng handa nang object
  • Pagsisimula — ang mga parameter sa @StateObject ay kinakalkula nang isang beses, ang mga update ay hindi sinusubaybayan
  • Deinit — sapilitang paglilinis ng mga timer at subscription sa deinit ng ObservableObject
  • iOS 14+ — @StateObject ay magagamit simula sa iOS 14, macOS 11, watchOS 7, tvOS 14

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din